NAV Navbar
Rust Go Python Scala C# C++ Node.js Java

Introduction

This document describes Crypto service's gRPC API (and potential other future interfaces).

gRPC is a remote procedure call system developed at Google in 2015. The RPC requests are transported over HTTP2 and can be encoded in various ways, including protocol buffers.

Example features supported by a compliant gRPC implementation:

Cryptographic API

The cryptographic API provides access to cryptographic capabilities and security by using Hardware Security Modules (HSM) to process requests.

The current API named subtle is intended for people familiar with cryptographic primitives and best practices. Note: An easier-to-use API named Easy is also provided in the same proto file as a secondary gRPC service. It allows to Encrypt, Decrypt, Sign and Verify data without the need to know anything about the algorithms at play.

Note that the features proposed are not available in all the backends, you can find features matrixes in the features chapter after it.

Backends

The term backend is used to refer to our implementations of cryptographic providers. These are used behind the scenes to process the requests made to the API.

The cryptographic API does not depend on a specific backend implementation to be able to offer most of its features. Since backends can use various types of hardware (or software) to provide their functionalities, some API features will only work with a handful of backends, whereas some other features will work with most backends.

Production use-cases should always use backends that are based on hardware. Software backends exist to facilitate development and integration testing.

The list of currently supported backends is the following:

Please note that this choice is sometimes ignored. For instance, if you generate a secret key using a specific hardware backend, then only that backend will be able to use that key for cryptographic operations. In those cases, the backend is selected automatically depending on the context.

Some operations support explicit selection of a backend, if multiple ones are configured. You can select which backend to force by setting the value of the x-arcaos-crypto-backend gRPC header to the required backend's name.

Algorithms

These strings have similar semantic with TLS Cipher Strings

The API supports various algorithms described with strings. Strings can represent an algorithm with its length parameter, if the algorithm has a parameter, like SHA with 512 bits, or a combination of algorithms like ECDSA with SHA in 256 bits mode, i.e. "ECDSA_SHA_256".

Here are few other examples:

These strings are used to construct requests to cryptographic backends in operation like key generation and key operation.

Usage examples

API usage will be showcased using client-side examples in various programming languages. These are intended to help developers by introducing them to the API through small snippets of code.

Most of the time, there will be multiple ways to interact with a gRPC API within the ecosystem of a given language. The libraries and frameworks selected in the examples do not imply that they are the only ones that can be used. You may prefer to use something else depending on your use case.

Last but not least, the examples may not compile as is. They are meant to be used as guidelines, and most of them probably require some setup in order to work. Since gRPC is a popular and well documented RPC system, this document does not aim to explain how to do this setup.

Connecting to the API

use crypto::derive_wallet_master_key_request;
use crypto::derive_wallet_public_key_one_shot_request;
use crypto::derive_wallet_and_sign_one_shot_request;

use crypto::*;
use service::subtle_crypto_client::SubtleCryptoClient;

use anyhow::Result;
use std::{env, str};
use tokio::time::{sleep, Duration};
use tonic::metadata::MetadataValue;
use tonic::transport::Channel;
use tonic::Request;

#[tokio::main]
async fn main() -> Result<()> {
    // Open connection to the service with GRPC and creates a client
    let host = format!("https://{}", env::var("HOST")?);
    let channel = Channel::from_shared(host)?.connect().await?;

    let token = MetadataValue::from_str("Bearer 00000000-0000-0000-0000-000000000000")?;
    let mut client = SubtleCryptoClient::with_interceptor(channel, move |mut req: Request<()>| {
        req.metadata_mut().insert("authorization", token.clone());
        Ok(req)
    });

    // Do something with the client

    Ok(())
}
package main

import (
	"context"
	"fmt"
	"os"
	"time"

	"arcaos/api/crypto"
	"arcaos/api/service"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

func main() {
	// Open connection to the service with GRPC
	conn, err := grpc.Dial(os.Getenv("HOST"), grpc.WithInsecure())
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	// Create a client from the crypto service
	ctx := metadata.AppendToOutgoingContext(
		context.Background(),
		"Authorization",
		"Bearer 00000000-0000-0000-0000-000000000000",
	)
	client := service.NewSubtleCryptoClient(conn)

	// Do something with the service

}
import os
import sys
import time

import grpc

from service import crypto_pb2
from service import crypto_pb2_grpc

from crypto import *

metadata = [('authorization', 'Bearer 00000000-0000-0000-0000-000000000000')]


def run():
    with grpc.insecure_channel(os.environ['HOST']) as channel:
        stub = crypto_pb2_grpc.SubtleCryptoStub(channel)

        # Do something with the client

package sample

import arcaos.api.service._
import arcaos.api.crypto._

import akka.{Done, NotUsed}
import akka.actor.ActorSystem
import akka.grpc.GrpcClientSettings
import akka.grpc.SSLContextUtils
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Source
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.{Failure, Success}
import io.grpc.Metadata
import io.grpc.CallCredentials
import io.grpc.CallCredentials.{RequestInfo, MetadataApplier}
import io.grpc.Status

import com.google.protobuf.ByteString

object Sample {
  def main(args: Array[String]): Unit = {
    implicit val sys = ActorSystem("CryptoClient")
    implicit val mat = ActorMaterializer()
    implicit val ec = sys.dispatcher

    val cred: CallCredentials = new CallCredentials() {
      override def thisUsesUnstableApi(): Unit = {}

      override def applyRequestMetadata(
          requestInfo: RequestInfo,
          appExecutor: java.util.concurrent.Executor,
          applier: MetadataApplier
      ) = {
        appExecutor.execute(new Runnable() {
          override def run() = {
            try {
              val headers: Metadata = new Metadata()
              val authorizationKey: Metadata.Key[String] =
                Metadata.Key
                  .of("Authorization", Metadata.ASCII_STRING_MARSHALLER)
              headers
                .put(
                  authorizationKey,
                  "Bearer 00000000-0000-0000-0000-000000000000"
                )
              applier.apply(headers)
            } catch {
              case ex: Throwable =>
                applier.fail(Status.UNAUTHENTICATED.withCause(ex))
            }
          }
        });
      }
    }

    val host = System.getenv("HOST").split(":")

    // Connects to broker with a config file on src/main/resources/application.conf :
    /*
    akka.grpc.client {
      "sample.Sample" {
        host = arcaos-api
        port = 8023
        use-tls = false
      }
    }
     */
    val client = SubtleCryptoClient(
      GrpcClientSettings.fromConfig("sample.Sample").withCallCredentials(cred)
    )

    // Using a for comprehension to gather all upcoming API calls.
    val statusAll = for {

      // Do something with the client

    // End of for comprehension
    } yield ()
  }
}
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Service;
using Crypto;
using Google.Protobuf;
using System.Collections.Generic;

namespace crypto_grpc_csharp {
class Program {

  public static string Bytes2Hex(ByteString ba) {
    return "0x" + BitConverter.ToString(ba.ToByteArray()).Replace("-", "");
  }

  static void Main(string[] args) {
    // Follow the configuration:
    // https://docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-3.1&tabs=visual-studio-code
    // And add in .csproj:
    // <Protobuf Include="api/**/*.proto" ProtoRoot="./api/"
    // GrpcServices="Client" /> For the following to work you need to use
    // Grpc.Core package instead of Grpc.Net.Client
    var channel = new Channel(Environment.GetEnvironmentVariable("HOST"),
                              ChannelCredentials.Insecure);
    var client = new SubtleCrypto.SubtleCryptoClient(channel);

    var metadata = new Metadata();
    metadata.Add("Authorization",
                 "Bearer 00000000-0000-0000-0000-000000000000");

    // Do something with the client
    }
  }
}
#include <chrono>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <sstream>
#include <stdlib.h>
#include <string>
#include <thread>
#include <vector>

#include <grpcpp/grpcpp.h>

#include "crypto/hash.pb.h"
#include "crypto/rand.pb.h"
#include "service/crypto.grpc.pb.h"

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;

const std::string HEADER = "authorization";
const std::string TOKEN = "Bearer 00000000-0000-0000-0000-000000000000";

class SubtleCryptoClient {
public:
  SubtleCryptoClient(std::shared_ptr<Channel> channel)
      : stub_(service::SubtleCrypto::NewStub(channel)) {}
private:
  std::unique_ptr<service::SubtleCrypto::Stub> stub_;
};

void print_bytes(const std::string &msg, const std::string &data) {
  std::cout << msg;
  std::cout << std::hex;

  for (const char &c : data) {
    std::cout << std::setw(2) << std::setfill('0') << (0xFF & c);
  }

  std::cout << std::endl;
}

int main(int argc, char **argv) {
  // Instantiate the client. It requires a channel, out of which the actual RPCs
  // are created. This channel models a connection to an endpoint specified by
  // the argument "--target=" which is the only expected argument.
  // We indicate that the channel isn't authenticated (use of
  // InsecureChannelCredentials()).
  std::string target_str;
  std::string arg_str("--target");

  if (argc > 1) {
    std::string arg_val = argv[1];
    size_t start_pos = arg_val.find(arg_str);
    if (start_pos != std::string::npos) {
      start_pos += arg_str.size();
      if (arg_val[start_pos] == '=') {
        target_str = arg_val.substr(start_pos + 1);
      } else {
        std::cout << "The only correct argument syntax is --target="
                  << std::endl;
        return 0;
      }
    } else {
      std::cout << "The only acceptable argument is --target=" << std::endl;
      return 0;
    }
  } else {
    target_str = "arcaos-api:8023";
  }

  auto channel =
      grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials());

  SubtleCryptoClient subtle_crypto(channel);
  return 0;
}
  const path = require('path')
  const PROTO_PATH = path.join(__dirname, '/api/service/crypto.proto')
  const grpc = require('@grpc/grpc-js')
  const protoLoader = require('@grpc/proto-loader')

  const options = {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true,
    includeDirs: [path.join(__dirname, '/api/')]
  }

  const packageDefinition = protoLoader.loadSync(PROTO_PATH, options)
  const packageObject = grpc.loadPackageDefinition(packageDefinition)

  sleep()

  const service = packageObject.service
  const client = new service.SubtleCrypto(process.env.HOST, grpc.credentials.createInsecure())

  // metadata will have to be passed as the second argument to any client method
  const metadata = new grpc.Metadata()
  metadata.add('Authorization', 'Bearer 00000000-0000-0000-0000-000000000000')

  // Do something with the client

package api.grpc.cysec.systems;
import arcaos.api.crypto.Encrypt.*;
import arcaos.api.crypto.Generate.*;
import arcaos.api.crypto.Hash.*;
import arcaos.api.crypto.Mac.*;
import arcaos.api.crypto.Public.*;
import arcaos.api.crypto.KeyExchange.*;
import arcaos.api.crypto.Rand.*;
import arcaos.api.crypto.Sign.*;
import arcaos.api.crypto.Wallet.*;
import arcaos.api.service.SubtleCryptoGrpc;
import com.google.protobuf.ByteString;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.netty.NettyChannelBuilder;
import io.grpc.stub.MetadataUtils;
import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class App {
  static final Metadata.Key<String> AUTHORIZATION_KEY =
      Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);

  public static void main(String[] args) {
    ManagedChannel channel =
        NettyChannelBuilder.forTarget(System.getenv("HOST"))
            .usePlaintext()
            .build();

    SubtleCryptoGrpc.SubtleCryptoBlockingStub client =
        SubtleCryptoGrpc.newBlockingStub(channel);

    Metadata headers = new Metadata();
    headers.put(AUTHORIZATION_KEY,
                "Bearer 00000000-0000-0000-0000-000000000000");
    client = MetadataUtils.attachHeaders(client, headers);

    // Do something with the client

  }
}

In order to execute operations on the cryptographic service, a client using a gRPC framework depending on the language must be initialized. The gateway's IP address could change depending on the configuration, however the port number is 8023.

All requests are authenticated with the Authorization header and the value Bearer <token>.

On a physical appliance, you can generate a token with the command arcaos-ctl:

arcaos-ctl token create --client-id "Client" --comment "Comment for this token"

Once the client is initialized, it exposes all RPCs of the defined services, allowing operations with hardware backends, or software backends in testing environment.

The default backend is managed in the platform configuration, you can pin a specific backend with x-arcaos-crypto-backend but should refrain to do so if not required.

Hashing data

    // Hash of a message using sha256
    let response = client
        .hash(HashRequest {
            data: b"I want to hash this message".to_vec(),
            algorithm: "SHA_256".to_string(),
        })
        .await?;

    println!("Hash: {:?}", response.into_inner().digest);
	// Hash of a message using sha256
	msg := "I want to hash this message"

	rsp, err := client.Hash(ctx, &crypto.HashRequest{
		Algorithm: "SHA_256",
		Data:      []byte(msg),
	})

	// Check err for errors

	fmt.Printf("Hash: %x\n", rsp.Digest)
    msg = 'I want to hash this message'
    try:
        req = hash_pb2.HashRequest(algorithm='SHA_256', data=msg.encode())
        res = stub.Hash(request=req, metadata=metadata)
        print('Hash:', res.digest.hex())
    except Exception as err:
        print('There was an error hashing the message:', err)
      // Hash of a message using sha256
      val msg: String = "I want to hash this message"
      rspHash <-
        client
          .hash(
            HashRequest(
              ByteString.copyFromUtf8(msg),
              "SHA_256"
            )
          )
      _ = println("Hash: " + rspHash
                               .digest
                               .toByteArray()
                               .map("%02x" format _)
                               .mkString
                  )
    // Hashing payload
    var msg = "I want to hash this message";

    var replyHash =
        client.Hash(new HashRequest { Data = ByteString.CopyFromUtf8(msg),
                                      Algorithm = "SHA_256" },
                    metadata);

    Console.WriteLine("Hash: " + Bytes2Hex(replyHash.Digest));
  // Inside the public part of class SubtleCryptoClient
  std::string Hash(const std::string &data, const std::string &algorithm) {
    crypto::HashRequest request;
    request.set_data(data);
    request.set_algorithm(algorithm);

    crypto::HashResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->Hash(&context, request, &response);

    if (status.ok()) {
      return response.digest();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Hash of a message using sha256
  std::string hash_msg("I want to hash this message");
  std::string hash_response = subtle_crypto.Hash(hash_msg, "SHA_256");
  print_bytes("Hash: ", hash_response);
    // Hash of a message using sha256
    client.Hash(
      {
        data: Buffer.from('Message to digest', 'utf8'),
        algorithm: 'SHA_256'
      },
      metadata,
      (err, res) => {
        console.log(err || `Hash: ${res.digest.toString('hex')}`)
    }
  )
    // Hash of a message using sha256
    HashRequest reqHash =
        HashRequest.newBuilder()
            .setAlgorithm("SHA_256")
            .setData(ByteString.copyFromUtf8("I want to hash this message"))
            .build();

    HashResponse respHash = client.hash(reqHash);

    System.out.println("Hash: " + respHash.getDigest());

The hashing interface supports hashing blocks of bytes. The full data must be provided in the request, limiting the quantity of data that can be hashed at the moment.

HashRequest is used to ask the backend to hash a byte array of data with the given algorithm string identifier. See section about algorithms to understand how to construct such strings.

A streaming interface with initialize, update, and finalize is in the works.

In the given example bytes corresponding to the string "I want to hash this message" are hashed with SHA_256.

The response contains a digest field with the result of the hashing operation.

Generating randomness

    // Get 32 bytes of pseudo randomness (library)
    let response = client
        .get_entropy(GetEntropyRequest {
            source: "PRNG".to_string(),
            length: 32,
        })
        .await?;

    println!("Entropy: {:?}", response.into_inner().entropy);
	// Get 32 bytes of pseudo randomness (library)
	rsp, err := client.GetEntropy(ctx, &crypto.GetEntropyRequest{
		Source: "PRNG",
		Length: 32,
	})

	// Check err for errors

	fmt.Printf("Entropy: %x\n", rsp.Entropy)
    # Get 32 bytes of pseudo randomness (library)
    try:
        req = rand_pb2.GetEntropyRequest(source='PRNG', length=32)
        res = stub.GetEntropy(request=req, metadata=metadata)
        print('Entropy:', res.entropy.hex())
    except Exception as err:
        print('There was an error getting the entropy:', err)
      // Get 32 bytes of pseudo randomness (library)
      rspEntropy <-
        client
          .getEntropy(
            GetEntropyRequest(
              32,
              "PRNG"
            )
          )
      _ = println("Entropy: " + rspEntropy
                                  .entropy
                                  .toByteArray()
                                  .map("%02x" format _)
                                  .mkString
                  )
    // Get 32 bytes of pseudo randomness (library)
    var replyEntropy = client.GetEntropy(
        new GetEntropyRequest { Length = 32, Source = "PRNG" }, metadata);

    Console.WriteLine("Entropy: " + Bytes2Hex(replyEntropy.Entropy));
  // Inside the public part of class SubtleCryptoClient
  std::string GetEntropy(const std::string &source, const int length) {
    crypto::GetEntropyRequest request;
    request.set_source(source);
    request.set_length(length);

    crypto::GetEntropyResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->GetEntropy(&context, request, &response);

    if (status.ok()) {
      return response.entropy();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Get 32 bytes of pseudo randomness (library)
  std::string rand_response = subtle_crypto.GetEntropy("PRNG", 32);
  print_bytes("Entropy: ", rand_response);
    // Get 32 bytes of pseudo randomness (library)
    client.GetEntropy(
      {
        length: 32,
        source: 'PRNG'
      },
      metadata,
      (err, res) => {
        console.log(err || `Entropy: ${res.entropy.toString('hex')}`)
    }
  )
    // Get 32 bytes of pseudo randomness (library)
    GetEntropyRequest reqEntropy =
        GetEntropyRequest.newBuilder().setSource("PRNG").setLength(32).build();

    GetEntropyResponse respEntropy = client.getEntropy(reqEntropy);

    System.out.println("Entropy: " + respEntropy.getEntropy());

Random bytes, or random numbers, can be retrieved from three different sources:

Depending on the configured cryptographic backends, the TRNG and QRNG options might not be available.

In the given example 32 bytes of entropy are retrieved from a pseudo random number generator. The response contains an entropy field with the random bytes.

During development, and for some use cases during production, PRNG is enough for entropy needs.

Generating keys

    // Generate a key on elliptic curve SECP256K1, this key will be able to sign
    // according to ECDSA algorithm using SHA256 or SHA512 digest.
    let response = client
        .generate_key(GenerateKeyRequest {
            algorithm: "EC_SECP256K1".to_string(),
            whitelist: vec!["ECDSA_SHA_256".to_string(), "ECDSA_SHA_512".to_string(), "ECDH".to_string()],
            delete_after: String::new(),
        })
        .await?;

    let key_id_ec = response.into_inner().key_id;

    println!("EC Key id: {:?}", key_id_ec);

    // Generate a key for AES encryption and decryption, and for message
    // authentication using HMAC and SHA512.
    let response = client
        .generate_key(GenerateKeyRequest {
            algorithm: "CIPHER_256".to_string(),
            whitelist: vec!["AES_CBC_PKCS7".to_string(), 
                            "AES_GCM".to_string(), 
                            "HMAC_SHA_512".to_string()],
            delete_after: String::new(),
        })
        .await?;

    let key_id_aes = response.into_inner().key_id;

    println!("AES Key id: {:?}", key_id_aes);

    // Generate a key for RSA encryption and decryption
    let response = client
        .generate_key(GenerateKeyRequest {
            algorithm: "RSA_2048".to_string(),
            whitelist: vec!["RSAES_OAEP".to_string()],
            delete_after: String::new(),
        })
        .await?;

    let key_id_rsa = response.into_inner().key_id;

    println!("RSA Key id: {:?}", key_id_rsa);
	// Generate a key on elliptic curve SECP256K1, this key will be able to sign
	// according to ECDSA algorithm using a SHA256 digest.
	rspEc, err := client.GenerateKey(ctx, &crypto.GenerateKeyRequest{
		Algorithm: "EC_SECP256K1",
		Whitelist: []string{"ECDSA_SHA_256", "ECDH"},
	})

	// Check err for errors

	keyIdEc := rspEc.KeyId
	fmt.Printf("EC Key id: %s\n", keyIdEc)

	// Generate a key for AES encryption and decryption, and for message
	// authentication using HMAC and SHA512.
	rspAes, err := client.GenerateKey(ctx, &crypto.GenerateKeyRequest{
		Algorithm: "CIPHER_256",
		Whitelist: []string{"AES_CBC_PKCS7", "AES_GCM", "HMAC_SHA_512"},
	})

	// Check err for errors

	keyIdAes := rspAes.KeyId
	fmt.Printf("AES Key id: %s\n", keyIdAes)

	// Generate a key for RSA encryption and decryption
	rspRsa, err := client.GenerateKey(ctx, &crypto.GenerateKeyRequest{
		Algorithm: "RSA_2048",
		Whitelist: []string{"RSAES_OAEP"},
	})

	// Check err for errors

	keyIdRsa := rspRsa.KeyId
	fmt.Printf("RSA Key id: %s\n", keyIdRsa)

    try:
        # Generate a key on elliptic curve SECP256K1, this key will be able to sign
        # according to ECDSA algorithm using a SHA256 digest.
        req = generate_pb2.GenerateKeyRequest(algorithm='EC_SECP256K1',
                                              whitelist=['ECDSA_SHA_256', 'ECDH'])
        res = stub.GenerateKey(request=req, metadata=metadata)
        key_id_ec = res.key_id
        print('EC Key id:', key_id_ec)

        # Generate a key for AES encryption and decryption, and for message
        # authentication using HMAC and SHA512.
        req = generate_pb2.GenerateKeyRequest(
            algorithm='CIPHER_256', whitelist=['AES_CBC_PKCS7', 'AES_GCM', 'HMAC_SHA_512'])
        res = stub.GenerateKey(request=req, metadata=metadata)
        key_id_aes = res.key_id
        print('AES Key id:', key_id_aes)

        # Generate a key for RSA encryption and decryption
        req = generate_pb2.GenerateKeyRequest(algorithm='RSA_2048',
                                              whitelist=['RSAES_OAEP'])
        res = stub.GenerateKey(request=req, metadata=metadata)
        key_id_rsa = res.key_id
        print('RSA Key id:', key_id_rsa)

    except Exception as err:
        print('There was an error generating the key:', err)
      // Generate a key on elliptic curve SECP256K1, this key will be able to sign
      // according to ECDSA algorithm using a SHA256 or SHA512 digest.
      rspGenEc <-
        client
          .generateKey(
            GenerateKeyRequest(
              "EC_SECP256K1",
              Seq("ECDSA_SHA_256", "ECDH")
            )
          )
      _ = println("EC Key id: " + rspGenEc.keyId)

      // Generate a key for AES encryption and decryption, and for message
      // authentication using HMAC and SHA512.
      rspGenAes <-
        client
          .generateKey(
            GenerateKeyRequest(
              "CIPHER_256",
              Seq("AES_CBC_PKCS7", "AES_GCM", "HMAC_SHA_512")
            )
          )
      _ = println("AES Key id: " + rspGenAes.keyId)

      // Generate a key for RSA encryption and decryption
      rspGenRsa <- client
        .generateKey(
          GenerateKeyRequest(
            "RSA_2048",
            Seq("RSAES_OAEP")
          )
        )
      _ = println("RSA Key id: " + rspGenRsa.keyId)
    // Generate a key on elliptic curve SECP256K1, this key will be able to sign
    // according to ECDSA algorithm using a SHA256 digest.
    var reqGenEc = new GenerateKeyRequest { Algorithm = "EC_SECP256K1",
                                            Whitelist = { "ECDSA_SHA_256", "ECDH" } };

    var keyIdEc = client.GenerateKey(reqGenEc, metadata).KeyId;

    Console.WriteLine("EC Key ID: " + keyIdEc);

    // Generate a key for AES encryption and decryption, and for message
    // authentication using HMAC and SHA512.
    var reqGenAes =
        new GenerateKeyRequest { Algorithm = "CIPHER_256",
                                 Whitelist = { "AES_CBC_PKCS7", "AES_GCM", "HMAC_SHA_512" } };

    var keyIdAes = client.GenerateKey(reqGenAes, metadata).KeyId;

    Console.WriteLine("AES Key ID: " + keyIdAes);

    // Generate a key for RSA encryption and decryption
    var reqGenRsa = new GenerateKeyRequest { Algorithm = "RSA_2048",
                                             Whitelist = { "RSAES_OAEP" } };

    var keyIdRsa = client.GenerateKey(reqGenRsa, metadata).KeyId;

    Console.WriteLine("RSA Key ID: " + keyIdRsa);
  // Inside the public part of class SubtleCryptoClient
  std::string GenerateKey(const std::string &algorithm,
                          const std::vector<std::string> &whitelist) {
    crypto::GenerateKeyRequest request;
    request.set_algorithm(algorithm);
    *request.mutable_whitelist() = {whitelist.begin(), whitelist.end()};

    crypto::GenerateKeyResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->GenerateKey(&context, request, &response);

    if (status.ok()) {
      return response.key_id();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  std::vector<std::string> whitelist_ec;
  whitelist_ec.push_back("ECDSA_SHA_256");
  whitelist_ec.push_back("ECDH");
  std::string key_id_ec =
      subtle_crypto.GenerateKey("EC_SECP256K1", whitelist_ec);
  std::cout << "EC Key id: " << key_id_ec << std::endl;

  std::vector<std::string> whitelist_aes;
  whitelist_aes.push_back("AES_CBC_PKCS7");
  whitelist_aes.push_back("AES_GCM");
  whitelist_aes.push_back("HMAC_SHA_512");
  std::string key_id_aes =
      subtle_crypto.GenerateKey("CIPHER_256", whitelist_aes);
  std::cout << "AES Key id: " << key_id_aes << std::endl;

  std::vector<std::string> whitelist_rsa;
  whitelist_rsa.push_back("RSAES_OAEP");
  std::string key_id_rsa = subtle_crypto.GenerateKey("RSA_2048", whitelist_rsa);
  std::cout << "RSA Key id: " << key_id_rsa << std::endl;
    // Generate a key on elliptic curve SECP256K1, this key will be able to sign
    // according to ECDSA algorithm using a SHA256 digest.
    client.GenerateKey(
      {
        algorithm: 'EC_SECP256K1',
        whitelist: [
          'ECDSA_SHA_256',
          'ECDH'
        ]
      },
      metadata,
      (err, res) => {
        const keyIdEc = res.key_id
        console.log(err || `EC Key ID ${keyIdEc}`)
    }
  )
    // Generate a key for RSA encryption and decryption
    client.GenerateKey(
      {
        algorithm: 'CIPHER_256',
        whitelist: [
          'AES_CBC_PKCS7',
          'AES_GCM',
          'HMAC_SHA_512'
        ]
      },
      metadata,
      (err, res) => {
        const keyIdAes = res.key_id
        console.log(err || `AES Key ID ${keyIdAes}`)
    }
  )
    // Generate a key on elliptic curve SECP256K1, this key will be able to sign
    // according to ECDSA algorithm using a SHA256 digest.
    GenerateKeyRequest reqGenEc = GenerateKeyRequest.newBuilder()
                                        .setAlgorithm("EC_SECP256K1")
                                        .addWhitelist("ECDSA_SHA_256")
                                        .addWhitelist("ECDH")
                                        .build();

    GenerateKeyResponse respGenEc = client.generateKey(reqGenEc);

    System.out.println("EC Key ID: " + respGenEc.getKeyId());

    // Generate a key for AES encryption and decryption, and for message
    // authentication using HMAC and SHA512.
    GenerateKeyRequest reqGenAes = GenerateKeyRequest.newBuilder()
                                        .setAlgorithm("CIPHER_256")
                                        .addWhitelist("AES_CBC_PKCS7")
                                        .addWhitelist("AES_GCM")
                                        .addWhitelist("HMAC_SHA_512")
                                        .build();

    GenerateKeyResponse respGenAes = client.generateKey(reqGenAes);

    System.out.println("AES Key ID: " + respGenAes.getKeyId());

    // Generate a key for RSA encryption and decryption
    GenerateKeyRequest reqGenRsa = GenerateKeyRequest.newBuilder()
                                        .setAlgorithm("RSA_2048")
                                        .addWhitelist("RSAES_OAEP")
                                        .build();

    GenerateKeyResponse respGenRsa = client.generateKey(reqGenRsa);

    System.out.println("RSA Key ID: " + respGenRsa.getKeyId());

    // Generate a wrapped key for EC
    GenerateWrappedKeyRequest reqGenWrapEc = GenerateWrappedKeyRequest.newBuilder()
                                        .setAlgorithm("EC_SECP256K1")
                                        .build();

    GenerateWrappedKeyResponse respGenWrapEc = client.generateWrappedKey(reqGenWrapEc);

    System.out.println("EC Wrapped Key: " + respGenWrapEc.getWrappedSecret());
    System.out.println("EC Public Key: " + respGenWrapEc.getPublic());

    // Generate a key for AES encryption and decryption
    GenerateWrappedKeyRequest reqGenWrapAes = GenerateWrappedKeyRequest.newBuilder()
                                        .setAlgorithm("CIPHER_256")
                                        .build();

    GenerateWrappedKeyResponse respGenWrapAes = client.generateWrappedKey(reqGenWrapAes);

    System.out.println("AES Wrapped Key: " + respGenWrapAes.getWrappedSecret());
    //Public field always exists, but is empty for symmetric key generation algorithms
    if (respGenWrapAes.getPublic().size() != 0) {
        throw new IllegalStateException("Public field is not empty !");
    }


    // Sign a message using the previously generated key
    SignRequest reqSign =
        SignRequest.newBuilder()
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .setAlgorithm("ECDSA_SHA_256")
            .setKeyId(respGenEc.getKeyId())
            .build();

    SignResponse respSign = client.sign(reqSign);

    System.out.println("Signature: " + respSign.getSignature());

    // Sign a message using the previously generated wrapped key
    SignWithWrappedRequest reqSignWithWrapped =
    SignWithWrappedRequest.newBuilder()
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .setAlgorithm("ECDSA_SHA_256")
            .setWrappedKey(respGenWrapEc.getWrappedSecret())
            .build();

    SignResponse respSignWithWrapped = client.signWithWrapped(reqSignWithWrapped);

    System.out.println("Signature With Wrapped: " + respSignWithWrapped.getSignature());

    // Get the public key linked to the key id previously generated
    GetPublicComponentRequest reqPub = GetPublicComponentRequest.newBuilder()
                                           .setKeyId(respGenEc.getKeyId())
                                           .build();

    GetPublicComponentResponse respPub = client.getPublicComponent(reqPub);

    System.out.println("Public Key: " + respPub.getPublic());

    // Verify the signature of the message
    VerifySignatureRequest reqVerify =
        VerifySignatureRequest.newBuilder()
            .setKey(respPub.getPublic())
            .setAlgorithm("ECDSA_SHA_256")
            .setSignature(respSign.getSignature())
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .build();

    VerifySignatureResponse respVerify = client.verifySignature(reqVerify);

    System.out.println("Signature is valid: " + respVerify.getValid());

    // Verify the signature with wrapped
    VerifySignatureRequest reqVerifyWithWrapped =
        VerifySignatureRequest.newBuilder()
            .setKey(respGenWrapEc.getPublic())
            .setAlgorithm("ECDSA_SHA_256")
            .setSignature(respSignWithWrapped.getSignature())
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .build();

    VerifySignatureResponse respVerifyWithWrapped = client.verifySignature(reqVerifyWithWrapped);

    System.out.println("Signature from wrapped key is valid: " + respVerifyWithWrapped.getValid());

    // Encrypt some payload with AES
    EncryptRequest reqEncrypt =
        EncryptRequest.newBuilder()
            .setKeyId(respGenAes.getKeyId())
            .setAlgorithm("AES_CBC_PKCS7")
            .setPlaintext(ByteString.copyFromUtf8("Message to encrypt"))
            .setIv(ByteString.EMPTY)
            .build();

    EncryptResponse respEncrypt = client.encrypt(reqEncrypt);

    System.out.println("Cipher Text: " + respEncrypt.getCiphertext());
    System.out.println("IV: " + respEncrypt.getIv());

    // Decrypt the payload with AES
    DecryptRequest reqDecrypt = DecryptRequest.newBuilder()
                                    .setKeyId(respGenAes.getKeyId())
                                    .setAlgorithm("AES_CBC_PKCS7")
                                    .setCiphertext(respEncrypt.getCiphertext())
                                    .setIv(respEncrypt.getIv())
                                    .build();

    DecryptResponse respDecrypt = client.decrypt(reqDecrypt);

    System.out.println("Plain Text: " +
                       respDecrypt.getPlaintext().toStringUtf8());

    // Encrypt some payload with AES-GCM
    EncryptAeadRequest reqEncryptAead =
        EncryptAeadRequest.newBuilder()
            .setKeyId(respGenAes.getKeyId())
            .setAlgorithm("AES_GCM")
            .setPlaintext(
                ByteString.copyFromUtf8("Message to encrypt and authenticate"))
            .setAad(
                ByteString.copyFromUtf8("And this will be authenticated only"))
            .setIv(ByteString.EMPTY)
            .build();

    EncryptAeadResponse respEncryptAead = client.encryptAead(reqEncryptAead);

    System.out.println("Cipher Text: " + respEncryptAead.getCiphertext());
    System.out.println("IV: " + respEncryptAead.getIv());
    System.out.println("Tag: " + respEncryptAead.getAuthenticationTag());

    // Decrypt the payload with AES_GCM
    DecryptAeadRequest reqDecryptAead =
        DecryptAeadRequest.newBuilder()
            .setKeyId(respGenAes.getKeyId())
            .setAlgorithm("AES_GCM")
            .setCiphertext(respEncryptAead.getCiphertext())
            .setAad(
                ByteString.copyFromUtf8("And this will be authenticated only"))
            .setAuthenticationTag(respEncryptAead.getAuthenticationTag())
            .setIv(respEncryptAead.getIv())
            .build();

    DecryptAeadResponse respDecryptAead = client.decryptAead(reqDecryptAead);

    System.out.println("Plain Text: " +
                       respDecryptAead.getPlaintext().toStringUtf8());

    // Generate Bob keys (other side)
    GenerateKeyRequest reqGenBobEc =
        GenerateKeyRequest.newBuilder()
            .setAlgorithm("EC_SECP256K1")
            .addWhitelist("ECDH")
            .build();
    GenerateKeyResponse respGenBobEc = client.generateKey(reqGenBobEc);
    GetPublicComponentRequest reqBobPub =
        GetPublicComponentRequest.newBuilder()
            .setKeyId(respGenBobEc.getKeyId())
            .build();
    GetPublicComponentResponse respBobPub = client.getPublicComponent(reqBobPub);
    ByteString bobPublicKey = respBobPub.getPublic();

    //Derive share secret with ECDH
    DeriveEcdhRequest reqEcdh =
        DeriveEcdhRequest.newBuilder()
            .setKeyId(respGenEc.getKeyId())
            .setPublicKey(bobPublicKey)
            .build();
    DeriveEcdhResponse respEcdh = client.deriveEcdh(reqEcdh);
    System.out.println("Shared Secret: " + respEcdh.getSharedSecret());

    //Derive share secret with ECDH
    DeriveEcdhWithWrappedRequest reqEcdhWithWrapped =
        DeriveEcdhWithWrappedRequest.newBuilder()
            .setWrappedSecret(respGenWrapEc.getWrappedSecret())
            .setPublicKey(bobPublicKey)
            .build();
    DeriveEcdhResponse respEcdhWithWrapped = client.deriveEcdhWithWrapped(reqEcdhWithWrapped);
    System.out.println("Shared Secret: " + respEcdhWithWrapped.getSharedSecret());

    /*
    // Encrypt some payload with RSA
    EncryptRequest reqEncrypt = EncryptRequest.newBuilder()
        .setKeyId(respGenRsa.getKeyId())
        .setAlgorithm("RSAES_OAEP")
        .setPlaintext(ByteString.copyFromUtf8("Message to encrypt"))
        .build();

    EncryptResponse respEncrypt = client.publicEncrypt(reqEncrypt);

    System.out.println("Cipher Text: " + respEncrypt.getCiphertext());
    */

    /*
    // Decrypt the payload with RSA
    DecryptRequest reqDecrypt = DecryptRequest.newBuilder()
        .setKeyId(respGenRsa.getKeyId())
        .setAlgorithm("RSAES_OAEP")
        .setCiphertext(respEncrypt.getCiphertext())
        .build();

    DecryptResponse respDecrypt = client.decrypt(reqDecrypt);

    System.out.println("Plain Text: " + respDecrypt.getPlaintext());
    */

    GenerateWalletSeedRequest reqSeed =
        GenerateWalletSeedRequest.newBuilder().setLength(32).build();

    GenerateWalletSeedResponse respSeed = client.generateWalletSeed(reqSeed);

    DeriveWalletMasterKeyRequest reqMaster =
        DeriveWalletMasterKeyRequest.newBuilder()
            .setAlgorithm("BIP32")
            .addWhitelist("ECDSA_SHA_512")
            .setWrapped(respSeed.getWrappedSeed())
            .build();

    /*
    It is possible to use a plaintext seed

    GetEntropyRequest reqEntropyWallet =
    GetEntropyRequest.newBuilder().setSource("PRNG").setLength(64).build();

    GetEntropyResponse respEntropyWallet = client.getEntropy(reqEntropyWallet);

    ...
    .setPlaintext(respEntropyWallet.getEntropy())
    ...
    */

    DeriveWalletMasterKeyResponse respMaster =
        client.deriveWalletMasterKey(reqMaster);
    System.out.println("Master key id : " + respMaster.getKeyId());

    DeriveWalletPrivateKeyRequest reqPrivate =
        DeriveWalletPrivateKeyRequest.newBuilder()
            .setKeyId(respMaster.getKeyId())
            .addWhitelist("ECDSA_SHA_512")
            .addPath(2)
            .addPath(4)
            .addPath(1)
            .build();

    DeriveWalletPrivateKeyResponse respPrivate =
        client.deriveWalletPrivateKey(reqPrivate);
    System.out.println("Private key id : " + respPrivate.getKeyId());

    SignRequest reqSignWallet =
        SignRequest.newBuilder()
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .setAlgorithm("ECDSA_SHA_512")
            .setKeyId(respPrivate.getKeyId())
            .build();

    SignResponse respSignWallet = client.sign(reqSignWallet);

    System.out.println("Signature: " + respSignWallet.getSignature());

    GetWalletPublicComponentsRequest reqPubWallet =
        GetWalletPublicComponentsRequest.newBuilder()
            .setKeyId(respMaster.getKeyId())
            .build();

    GetWalletPublicComponentsResponse respPubWallet =
        client.getWalletPublicComponents(reqPubWallet);
    System.out.println("Master Public key : " + respPubWallet.getPublic());
    System.out.println("Master Chaincode : " + respPubWallet.getChainCode());
    System.out.println("Master Fingerprint : " +
                       respPubWallet.getParentFingerprint());
    System.out.println("Master Index key : " + respPubWallet.getIndex());
    System.out.println("Depth of master key : " + respPubWallet.getDepth());

    DeriveWalletPublicKeyRequest reqPublicKeyWallet =
        DeriveWalletPublicKeyRequest.newBuilder()
            .setAlgorithm("BIP32")
            .setPublicKey(respPubWallet.getPublic())
            .setChainCode(respPubWallet.getChainCode())
            .addPath(2)
            .addPath(4)
            .addPath(1)
            .build();

    DeriveWalletPublicKeyResponse respPublicKeyWallet =
        client.deriveWalletPublicKey(reqPublicKeyWallet);
    System.out.println("Public key : " + respPublicKeyWallet.getPublicKey());
    System.out.println("Chaincode key : " + respPublicKeyWallet.getChainCode());

    VerifySignatureRequest reqVerifyWallet =
        VerifySignatureRequest.newBuilder()
            .setKey(respPublicKeyWallet.getPublicKey())
            .setAlgorithm("ECDSA_SHA_512")
            .setSignature(respSignWallet.getSignature())
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .build();

    VerifySignatureResponse respVerifyWallet =
        client.verifySignature(reqVerifyWallet);

    System.out.println("Signature is valid: " + respVerifyWallet.getValid());

    GenerateWalletSeedRequest reqSeedOneShot =
        GenerateWalletSeedRequest.newBuilder().setLength(32).build();

    GenerateWalletSeedResponse respSeedOneShot = client.generateWalletSeed(reqSeedOneShot);

    DeriveWalletPublicKeyOneShotRequest reqPublicKeyOneShot =
    DeriveWalletPublicKeyOneShotRequest.newBuilder()
            .setAlgorithm("BIP32")
            .setWrapped(respSeedOneShot.getWrappedSeed())
            .addPath(4)
            .addPath(8)
            .addPath(2)
            .build();

    DeriveWalletPublicKeyOneShotResponse respPublicKeyOneShot =
        client.deriveWalletPublicKeyOneShot(reqPublicKeyOneShot);
    System.out.println("Public key is : " + respPublicKeyOneShot.getPublicKey());
    System.out.println("Chain code is : " + respPublicKeyOneShot.getChainCode());

    DeriveWalletAndSignOneShotRequest reqSignOneShot =
    DeriveWalletAndSignOneShotRequest.newBuilder()
            .setAlgorithm("BIP32")
            .setWrapped(respSeedOneShot.getWrappedSeed())
            .addPath(4)
            .addPath(8)
            .addPath(2)
            .setSignatureAlgorithm("ECDSA_SHA_512")
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .build();

    DeriveWalletAndSignOneShotResponse respSignOneShot =
        client.deriveWalletAndSignOneShot(reqSignOneShot);
    System.out.println("Signature is : " + respSignOneShot.getSignature());

    VerifySignatureRequest reqVerifyOneShot =
    VerifySignatureRequest.newBuilder()
        .setKey(respPublicKeyOneShot.getPublicKey())
        .setAlgorithm("ECDSA_SHA_512")
        .setSignature(respSignOneShot.getSignature())
        .setData(ByteString.copyFromUtf8("Message to sign"))
        .build();

    VerifySignatureResponse respVerifyOneShot =
        client.verifySignature(reqVerifyOneShot);

    System.out.println("Signature is valid: " + respVerifyOneShot.getValid());

    // Authenticate a message
    AuthenticateMessageRequest reqAuthenticateMessage =
        AuthenticateMessageRequest.newBuilder()
            .setKeyId(respGenAes.getKeyId())
            .setAlgorithm("HMAC_SHA_512")
            .setPlaintext(ByteString.copyFromUtf8("I want to authenticate this message"))
            .build();
    
    AuthenticateMessageResponse respAuthenticateMessage =
        client.authenticateMessage(reqAuthenticateMessage);

    System.out.println("Authentication tag: " + respAuthenticateMessage.getTag());

    // Verify a tag
    VerifyTagRequest reqVerifyTag =
        VerifyTagRequest.newBuilder()
            .setKeyId(respGenAes.getKeyId())
            .setAlgorithm("HMAC_SHA_512")
            .setPlaintext(ByteString.copyFromUtf8("I want to authenticate this message"))
            .setTag(respAuthenticateMessage.getTag())
            .build();
    
    VerifyTagResponse respVerifyTag =
        client.verifyTag(reqVerifyTag);

    System.out.println("Authentication tag is valid: " + respVerifyTag.getSuccess());

    // Authenticate a message with wrapped key
    AuthenticateMessageWithWrappedRequest reqAuthenticateMessageWithWrapped =
        AuthenticateMessageWithWrappedRequest.newBuilder()
            .setWrappedKey(respGenWrapAes.getWrappedSecret())
            .setAlgorithm("HMAC_SHA_512")
            .setPlaintext(ByteString.copyFromUtf8("I want to authenticate this message"))
            .build();
    
    AuthenticateMessageResponse respAuthenticateMessageWithWrapped =
        client.authenticateMessageWithWrapped(reqAuthenticateMessageWithWrapped);

    System.out.println("Authentication tag: " + respAuthenticateMessage.getTag());

    // Verify a tag with wrapped key
    VerifyTagWithWrappedRequest reqVerifyTagWithWrapped =
        VerifyTagWithWrappedRequest.newBuilder()
            .setWrappedKey(respGenWrapAes.getWrappedSecret())
            .setAlgorithm("HMAC_SHA_512")
            .setPlaintext(ByteString.copyFromUtf8("I want to authenticate this message"))
            .setTag(respAuthenticateMessageWithWrapped.getTag())
            .build();
    
    VerifyTagResponse respVerifyTagWithWrapped =
        client.verifyTagWithWrapped(reqVerifyTagWithWrapped);

    System.out.println("Authentication tag is valid: " + respVerifyTagWithWrapped.getSuccess());

    System.exit(0);
  }
}

Key generation is one of the most important operations in the cryptographic API. To understand the key management in ARCA we have to understand how keys are generated.

When a request for a key generation is sent, that request is processed by one specific backend, that backend is chosen either by the default configuration, or by pinning it with the x-arcaos-crypto-backend header. The generated key is tagged by the processing backend, thus that key can only be used by the tagged backend.

During key generation, in addition to the tagged backend, a whitelist of algorithms is passed to limit usage of that key only to whitelisted contexts. E.g. this key generated with RSA with a 4096 bits modulus size can only be used to do RSA signatures and not encryption.

In the given example a key for the elliptic curve secp256k1 is generated that can only perform ECDSA signatures with SHA in 256 bits and 512 bits mode.

The response returns a key_id. Those key ids serve as handles and MUST be stored by the application for future usage of the generated key.

Generating wrapped keys

    // Generate a wrapped key on elliptic curve SECP256K1
    let response = client
        .generate_wrapped_key(GenerateWrappedKeyRequest {
            algorithm: "EC_SECP256K1".to_string(),
        })
        .await?;

    let generated_keys = response.into_inner();
    let wrapped_key_ec = generated_keys.wrapped_secret;
    let wrapped_key_ec_pub = generated_keys.public;

    println!("Wrapped EC Key: {:?}", wrapped_key_ec);
    println!("Wrapped EC Public Key: {:?}", wrapped_key_ec_pub);

    // Generate a wrapped key for AES encryption and decryption
    let response = client
        .generate_wrapped_key(GenerateWrappedKeyRequest {
            algorithm: "CIPHER_256".to_string(),
        })
        .await?;

    let generated_keys = response.into_inner();
    let wrapped_key_aes = generated_keys.wrapped_secret;
    // Public field always exists, but is empty for symmetric key generation algorithms
    assert!(generated_keys.public.is_empty());

    println!("Wrapped AES Key: {:?}", wrapped_key_aes);

    // Generate a wrapped key for RSA encryption and decryption
    let response = client
        .generate_wrapped_key(GenerateWrappedKeyRequest {
            algorithm: "RSA_2048".to_string(),
        })
        .await?;

    let generated_keys = response.into_inner();
    let wrapped_key_rsa = generated_keys.wrapped_secret;
    let wrapped_key_rsa_pub = generated_keys.public;

    println!("Wrapped RSA Key: {:?}", wrapped_key_rsa);
    println!("Wrapped ERSAC Public Key: {:?}", wrapped_key_rsa_pub);
	// Generate a key on elliptic curve SECP256K1, this key will be able to sign
	// according to ECDSA algorithm using a SHA256 digest.
	rspEc, errEc := client.GenerateWrappedKey(ctx, &crypto.GenerateWrappedKeyRequest{
		Algorithm: "EC_SECP256K1",
	})

	// Check err for errors

	fmt.Printf("EC Wrapped Key: %x\n", rspEc.WrappedSecret)
	fmt.Printf("EC Public Key: %x\n", rspEc.Public)


	// Generate a key for AES encryption and decryption
	// For the examples we'll use same key for CBC and GCM
	rspAes, errAes := client.GenerateWrappedKey(ctx, &crypto.GenerateWrappedKeyRequest{
		Algorithm: "CIPHER_256",
	})

	// Check err for errors

	fmt.Printf("AES Wrapped Key: %x\n", rspAes.WrappedSecret)
	// Public field always exists, but is empty for symmetric key generation algorithms
	if len(rspAes.Public) != 0 {
		panic("Public key is not empty")
	}

    try:
        # Generate a key on elliptic curve SECP256K1
        req = generate_pb2.GenerateWrappedKeyRequest(algorithm='EC_SECP256K1')
        res = stub.GenerateWrappedKey(request=req, metadata=metadata)
        wrapped_key_ec     = res.wrapped_secret
        wrapped_key_ec_pub = res.public
        print('Wrapped EC Key: ', wrapped_key_ec);
        print('Wrapped EC Public Key: ', wrapped_key_ec_pub);

        # Generate a key for AES encryption and decryption
        req = generate_pb2.GenerateWrappedKeyRequest(algorithm='CIPHER_256')
        res = stub.GenerateWrappedKey(request=req, metadata=metadata)
        wrapped_key_aes = res.wrapped_secret
        print('Wrapped AES Key:', wrapped_key_aes)
        # Public field always exists, but is empty for symmetric key generation algorithms
        assert not res.public

        # Generate a key for RSA encryption and decryption
        req = generate_pb2.GenerateWrappedKeyRequest(algorithm='RSA_2048')
        res = stub.GenerateWrappedKey(request=req, metadata=metadata)
        wrapped_key_rsa     = res.wrapped_secret
        wrapped_key_rsa_pub = res.public
        print('Wrapped RSA Key: ', wrapped_key_rsa);
        print('Wrapped RSA Public Key: ', wrapped_key_rsa_pub);

    except Exception as err:
        print('There was an error generating the wrapped key:', err)
      rspGenWrappedEc <- client.generateWrappedKey(GenerateWrappedKeyRequest("EC_SECP256K1"))
      _ = println("Wrapped EC Key: " + rspGenWrappedEc.wrappedSecret)
      _ = println("Wrapped EC Public Key: " + rspGenWrappedEc.public)

      rspGenWrappedAes <- client.generateWrappedKey(GenerateWrappedKeyRequest("CIPHER_256"))
      _ = println("Wrapped AES Key: " + rspGenWrappedAes.wrappedSecret)
      // Public part always exist, but is empty for symmetric key generation algorithms
      _ = println("Wrapped AES Public Key: " + rspGenWrappedAes.public)
    // Generate a wrapped key on elliptic curve SECP256K1
    var reqGenWrappedEc =
        new GenerateWrappedKeyRequest { Algorithm = "EC_SECP256K1" };
    var respGenWrappedEc = client.GenerateWrappedKey(reqGenWrappedEc, metadata);

    Console.WriteLine("Wrappd EC Key: " +
                      Bytes2Hex(respGenWrappedEc.WrappedSecret));
    Console.WriteLine("Wrapped EC Public Key: " +
                      Bytes2Hex(respGenWrappedEc.Public));

    // Generate a key for AES encryption and decryption
    // For the examples we'll use same key for CBC and GCM
    var reqGenWrappedAes =
        new GenerateWrappedKeyRequest { Algorithm = "CIPHER_256" };
    var respGenWrappedAes =
        client.GenerateWrappedKey(reqGenWrappedAes, metadata);

    Console.WriteLine("Wrappd AES Key: " +
                      Bytes2Hex(respGenWrappedAes.WrappedSecret));
    Console.WriteLine("Wrapped AES Public Key: " +
                      Bytes2Hex(respGenWrappedAes.Public));

    // Public part always exist, but is empty for symmetric key generation
    // algorithms
    Debug.Assert(respGenWrappedAes.Public.Length == 0);
  // Inside the public part of class SubtleCryptoClient
  crypto::GenerateWrappedKeyResponse GenerateWrappedKey(const std::string &algorithm) {
    crypto::GenerateWrappedKeyRequest request;
    request.set_algorithm(algorithm);

    crypto::GenerateWrappedKeyResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->GenerateWrappedKey(&context, request, &response);

    if (status.ok()) {
      return response;
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  crypto::GenerateWrappedKeyResponse wrapped_key_ec =
      subtle_crypto.GenerateWrappedKey("EC_SECP256K1");
  print_bytes("Wrapped Key: ", wrapped_key_ec.wrapped_secret());
  print_bytes("Public Key: ", wrapped_key_ec.public_());

  crypto::GenerateWrappedKeyResponse wrapped_key_aes =
      subtle_crypto.GenerateWrappedKey("CIPHER_256");
  print_bytes("Wrapped Key: ", wrapped_key_aes.wrapped_secret());
  // Public field always exists, but is empty for symmetric key generation algorithms
  assert(wrapped_key_aes.public_().size() == 0);
    // Generate a key on elliptic curve SECP256K1, this key will be able to sign
    // according to ECDSA algorithm using a SHA256 digest.
    client.GenerateWrappedKey(
      {
        algorithm: 'EC_SECP256K1'
      },
      metadata,
      (err, res) => {
        const wrappedKeyEc = res.wrapped_secret
        const wrappedKeyEcPub = res.public
        console.log(err || `Wrapped EC Key ${wrappedKeyEc.toString('hex')}`)
        console.log(err || `Wrapped EC Public Key ${wrappedKeyEcPub.toString('hex')}`)
    }
  )
    // Generate a key for RSA encryption and decryption
    client.GenerateWrappedKey(
      {
        algorithm: 'CIPHER_256'
      },
      metadata,
      (err, res) => {
        const wrappedKeyAes = res.wrapped_secret
        console.log(res.public)
        assert(res.public.length === 0,
          'Public field always exists, but is empty for symmetric key generation algorithms')
        console.log(err || `Wrapped Aes Key ${wrappedKeyAes.toString('hex')}`)
    }
  )
    // Generate a wrapped key for EC
    GenerateWrappedKeyRequest reqGenWrapEc = GenerateWrappedKeyRequest.newBuilder()
                                        .setAlgorithm("EC_SECP256K1")
                                        .build();

    GenerateWrappedKeyResponse respGenWrapEc = client.generateWrappedKey(reqGenWrapEc);

    System.out.println("EC Wrapped Key: " + respGenWrapEc.getWrappedSecret());
    System.out.println("EC Public Key: " + respGenWrapEc.getPublic());

    // Generate a key for AES encryption and decryption
    GenerateWrappedKeyRequest reqGenWrapAes = GenerateWrappedKeyRequest.newBuilder()
                                        .setAlgorithm("CIPHER_256")
                                        .build();

    GenerateWrappedKeyResponse respGenWrapAes = client.generateWrappedKey(reqGenWrapAes);

    System.out.println("AES Wrapped Key: " + respGenWrapAes.getWrappedSecret());
    //Public field always exists, but is empty for symmetric key generation algorithms
    if (respGenWrapAes.getPublic().size() != 0) {
        throw new IllegalStateException("Public field is not empty !");
    }

This section describes how to generate a wrapped key that can be used on specific call suffixed with WithWrapped.

When a request for a wrapped key generation is sent, that request is processed by one specific backend, that backend is chosen either by the default configuration, or by pinning it with the x-arcaos-crypto-backend header.

This call differs from traditional key generation in that the generated key is never stored by the crypto service. The storage and management of the key is entirely up to the user. It is important to know that these keys do not contain any metadata that limits their use on the various operations offered by the crypto service. This private key is protected by the chosen backend. In the case of the software backend, the generated key are not protected, this backend is not recommended for production environment.

The response returns a wrapped_secret key and an optional public key. The public key is empty for symmetric algorithms.

Signing messages

    // Sign a message using the previously generated key
    let response = client
        .sign(SignRequest {
            data: b"I want to sign this message using my SECP256K1 key".to_vec(),
            key_id: key_id_ec.clone(),
            algorithm: "ECDSA_SHA_512".to_string(), // If not supported by the key, error out
        })
        .await?;

    let signature = response.into_inner().signature;

    println!("Signature: {:?}", signature);
    // Sign a message using the previously exported wrapped key
    let response = client
        .sign_with_wrapped(SignWithWrappedRequest {
            data: b"I want to sign this message using my SECP256K1 key".to_vec(),
            wrapped_key: wrapped_key_ec.clone(),
            algorithm: "ECDSA_SHA_512".to_string(), // If not supported by the key, error out
        })
        .await?;

    let signature_with_wrapped = response.into_inner().signature;

    println!("Signature with wrapped: {:?}", signature_with_wrapped);
	// Sign a message using the previously generated key
	msg := "I want to sign this message using my SECP256K1 key"
	rsp, err := client.Sign(ctx, &crypto.SignRequest{
		Algorithm: "ECDSA_SHA_256", // If not supported by the key, error out
		KeyId:     keyIdEc,
		Data:      []byte(msg),
	})

	// Check err for errors

	signature := rsp.Signature
	fmt.Printf("Signature: %x\n", signature)
	// Sign a message using the previously generated key
	msg := "I want to sign this message using my SECP256K1 wrapped key"
	rsp, err := client.SignWithWrapped(ctx, &crypto.SignWithWrappedRequest{
		Algorithm:  "ECDSA_SHA_256", // If not supported by the key, error out
		WrappedKey: wrappedKey,
		Data:       []byte(msg),
	})

	// Check err for errors

	signature := rsp.Signature
	fmt.Printf("Signature From Wrapped Key: %x\n", signature)
    # Sign a message using the previously generated key
    msg = 'I want to sign this message using my SECP256K1 key'
    try:
        req = sign_pb2.SignRequest(algorithm='ECDSA_SHA_256',
                                   key_id=key_id_ec,
                                   data=msg.encode())
        res = stub.Sign(request=req, metadata=metadata)
        signature = res.signature
        print('Signature:', signature.hex())
    except Exception as err:
        print('There was an error signing the message:', err)
    # Sign a message using the previously generated key
    try:
        req = sign_pb2.SignWithWrappedRequest(algorithm='ECDSA_SHA_256',
                                              wrapped_key=wrapped_key_ec,
                                              data=msg.encode())
        res = stub.SignWithWrapped(request=req, metadata=metadata)
        signature_with_wrapped = res.signature
        print('Signature:', signature.hex())
    except Exception as err:
        print('There was an error signing the message with wrapped key:', err)
      // Sign a message using the previously generated key
      val msg: String = "I want to sign this message using my SECP256K1 key"
      rspSign <-
        client
          .sign(
            SignRequest(
              ByteString.copyFromUtf8(msg),
              rspGenEc.keyId,
              "ECDSA_SHA_256"
            )
          )
      _ = println(
        "Signature: " + rspSign.signature
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
      val msg: String = "I want to sign this message using my wrapped SECP256K1 key"
      rspSignWithWrapped <-
        client
          .signWithWrapped(
            SignWithWrappedRequest(
              ByteString.copyFromUtf8(msg),
              rspGenWrappedEc.wrappedSecret,
              "ECDSA_SHA_256"
            )
          )
      _ = println(
        "Signature: " + rspSign.signature
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
    var msgToSign = "I want to sign this message using my SECP256K1 key";

    var replySign =
        client.Sign(new SignRequest { Algorithm = "ECDSA_SHA_256",
                                      Data = ByteString.CopyFromUtf8(msgToSign),
                                      KeyId = keyIdEc },
                    metadata);

    Console.WriteLine("Signature: " + Bytes2Hex(replySign.Signature));
    var msgToSignWithWrapped =
        "I want to sign this message using my wrapped SECP256K1 key";

    var replySignWithWrapped = client.SignWithWrapped(
        new SignWithWrappedRequest {
          Algorithm = "ECDSA_SHA_256",
          Data = ByteString.CopyFromUtf8(msgToSignWithWrapped),
          WrappedKey = respGenWrappedEc.WrappedSecret
        },
        metadata);

    Console.WriteLine("Signature: " +
                      Bytes2Hex(replySignWithWrapped.Signature));
  // Inside the public part of class SubtleCryptoClient
  std::string Sign(const std::string &algorithm, const std::string &key_id,
                   const std::string &data) {
    crypto::SignRequest request;
    request.set_algorithm(algorithm);
    request.set_key_id(key_id);
    request.set_data(data);

    crypto::SignResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->Sign(&context, request, &response);

    if (status.ok()) {
      return response.signature();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Sign a message using the previously generated key
  std::string sign_msg("I want to sign this message using my SECP256K1 key");
  std::string signature =
      subtle_crypto.Sign("ECDSA_SHA_256", key_id_ec, sign_msg);
  print_bytes("Signature: ", signature);
  // Inside the public part of class SubtleCryptoClient
  std::string SignWithWrapped(const std::string &algorithm, const std::string &wrapped_key,
                   const std::string &data) {
    crypto::SignWithWrappedRequest request;
    request.set_algorithm(algorithm);
    request.set_wrapped_key(wrapped_key);
    request.set_data(data);

    crypto::SignResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->SignWithWrapped(&context, request, &response);

    if (status.ok()) {
      return response.signature();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Sign a message using the previously generated wrapped key
  std::string sign_with_wrapped_msg("I want to sign this message using my SECP256K1 wrapped key");
  std::string signature_from_wrapped =
      subtle_crypto.SignWithWrapped("ECDSA_SHA_256", wrapped_key_ec.wrapped_secret(), sign_with_wrapped_msg);
  print_bytes("Signature from wrapped key: ", signature_from_wrapped);
    // Sign a message using the previously generated key
    client.Sign(
      {
        algorithm: 'ECDSA_SHA_256',
        data: Buffer.from('Message to sign', 'utf8'),
        key_id: keyIdEc
      },
      metadata,
      (err, resSign) => {
        console.log(err || `Signature: ${resSign.signature.toString('hex')}`)
    }
  )
    // Sign a message using the previously generated wrapped key
    client.SignWithWrapped(
      {
        algorithm: 'ECDSA_SHA_256',
        data: Buffer.from('Message to sign', 'utf8'),
        wrapped_key: wrappedKeyEc
      },
      metadata,
      (err, resSign) => {
        console.log(err || `Signature with wrapped: ${resSign.signature.toString('hex')}`)
    }
  )
    // Sign a message using the previously generated key
    SignRequest reqSign =
        SignRequest.newBuilder()
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .setAlgorithm("ECDSA_SHA_256")
            .setKeyId(respGenEc.getKeyId())
            .build();

    SignResponse respSign = client.sign(reqSign);

    System.out.println("Signature: " + respSign.getSignature());
    // Sign a message using the previously generated wrapped key
    SignWithWrappedRequest reqSignWithWrapped =
    SignWithWrappedRequest.newBuilder()
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .setAlgorithm("ECDSA_SHA_256")
            .setWrappedKey(respGenWrapEc.getWrappedSecret())
            .build();

    SignResponse respSignWithWrapped = client.signWithWrapped(reqSignWithWrapped);

    System.out.println("Signature With Wrapped: " + respSignWithWrapped.getSignature());

After generating a wrapped key or a key with signing capabilities one can perform signatures with a given algorithm on provided data.

If the key represented by the key_id does not exist, meaning its id does not exist or the key has not been generated by the currently pinned backend, or the key does not whitelist the selected algorithm an error is returned. If all conditions are fulfilled a signature is returned.

In the response the signature field contains the full signature. The format depends on the selected algorithm.

Interface for signing hashes directly, or streaming large messages could be provided in the future.

Getting public components

    // Get the public key linked to the key id previously generated
    let response = client
        .get_public_component(GetPublicComponentRequest {
            key_id: key_id_ec.clone(),
        })
        .await?;

    let public_key = response.into_inner().public;

    println!("Public Key: {:?}", public_key);
	// Get the public key linked to the key id previously generated
	rsp, err := client.GetPublicComponent(ctx, &crypto.GetPublicComponentRequest{
		KeyId: keyIdEc,
	})

	// Check err for errors

	publicKey := rsp.Public
	fmt.Printf("Public Key: %x\n", publicKey)
    # Get the public key linked to the key id previously generated
    try:
        req = public_pb2.GetPublicComponentRequest(key_id=key_id_ec)
        res = stub.GetPublicComponent(request=req, metadata=metadata)
        public_key = res.public
        print('Public Key: ', public_key.hex())
    except Exception as err:
        print('There was an error getting the public key:', err)
      // Get the public key linked to the key id previously generated
      rspPubkey <- client.getPublicComponent(
        GetPublicComponentRequest(rspGenEc.keyId)
      )
      _ = println(
        "Public Key: " + rspPubkey.public
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
    var publicKey =
        client
            .GetPublicComponent(
                new GetPublicComponentRequest { KeyId = keyIdEc }, metadata)
            .Public;

    Console.WriteLine("Public Key: " + Bytes2Hex(publicKey));
  // Inside the public part of class SubtleCryptoClient
  std::string GetPublicComponent(const std::string &key_id) {
    crypto::GetPublicComponentRequest request;
    request.set_key_id(key_id);

    crypto::GetPublicComponentResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->GetPublicComponent(&context, request, &response);

    if (status.ok()) {
      return response.public_();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Get the public key linked to the key id previously generated
  std::string public_key = subtle_crypto.GetPublicComponent(key_id_ec);
  print_bytes("Public Key: ", public_key);
    // Get the public key linked to the key id previously generated
    client.GetPublicComponent(
      {
        key_id: keyIdEc
      },
      metadata,
      (err, resPub) => {
        console.log(err || `Public key: ${resPub.public.toString('hex')}`)
    }
  )
    // Get the public key linked to the key id previously generated
    GetPublicComponentRequest reqPub = GetPublicComponentRequest.newBuilder()
                                           .setKeyId(respGenEc.getKeyId())
                                           .build();

    GetPublicComponentResponse respPub = client.getPublicComponent(reqPub);

    System.out.println("Public Key: " + respPub.getPublic());

In the case of asymmetric key generation (elliptic curves, RSA), the public part of a key pair can be retrieved using the key_id handle.

If GetPublicComponent is called on a key_id referencing a symmetric key, like AES or ChaCha, an error is returned.

Verifying signatures

    // Verify the signature of the message
    let response = client
        .verify_signature(VerifySignatureRequest {
            data: b"I want to sign this message using my SECP256K1 key".to_vec(),
            signature,
            key: public_key,
            algorithm: "ECDSA_SHA_512".to_string(),
        })
        .await?;

    println!("Signature is valid: {:?}", response.into_inner().valid);

    // Verify the signature of the message made out of wrapped EC key
    let response = client
        .verify_signature(VerifySignatureRequest {
            data: b"I want to sign this message using my SECP256K1 key".to_vec(),
            signature: signature_with_wrapped,
            key: wrapped_key_ec_pub,
            algorithm: "ECDSA_SHA_512".to_string(),
        })
        .await?;

    println!("Signature with wrapped is valid: {:?}", response.into_inner().valid);
	// Verify the signature of the message
	rsp, err := client.VerifySignature(ctx, &crypto.VerifySignatureRequest{
		Algorithm: "ECDSA_SHA_256",
		Key:       publicKey,
		Signature: signature,
		Data:      []byte(msg),
	})

	// Check err for errors

	valid := rsp.Valid
	fmt.Printf("Signature is valid: %t\n", valid)
    try:
        # Verify the signature of the message
        req = sign_pb2.VerifySignatureRequest(
            algorithm='ECDSA_SHA_256',
            key=public_key,
            signature=signature,
            data=msg.encode(),
        )
        res = stub.VerifySignature(request=req, metadata=metadata)
        print('Signature is valid:', res.valid)

        # Verify the signature of the message made out of wrapped EC key
        req = sign_pb2.VerifySignatureRequest(
            algorithm='ECDSA_SHA_256',
            key=wrapped_key_ec_pub,
            signature=signature_with_wrapped,
            data=msg.encode(),
        )
        res = stub.VerifySignature(request=req, metadata=metadata)
        print('Signature with wrapped is valid:', res.valid)
    except Exception as err:
        print('There was an error verifying the signature:', err)
      // Verify the signature of the message
      rspVerify <- client.verifySignature(
        VerifySignatureRequest(
          ByteString.copyFromUtf8(msg),
          rspSign.signature,
          rspPubkey.public,
          "ECDSA_SHA_256"
        )
      )
      _ = println(
        "Signature is valid: " + rspVerify.valid
      )
    var replyVerify = client.VerifySignature(
        new VerifySignatureRequest { Key = publicKey,
                                     Algorithm = "ECDSA_SHA_256",
                                     Signature = replySign.Signature,
                                     Data =
                                         ByteString.CopyFromUtf8(msgToSign) },
        metadata);

    Console.WriteLine("Signature is valid: " + replyVerify.Valid);
  // Inside the public part of class SubtleCryptoClient
  bool VerifySignature(const std::string &algorithm,
                       const std::string &public_key,
                       const std::string &signature, const std::string &data) {
    crypto::VerifySignatureRequest request;
    request.set_algorithm(algorithm);
    request.set_key(public_key);
    request.set_signature(signature);
    request.set_data(data);

    crypto::VerifySignatureResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->VerifySignature(&context, request, &response);

    if (status.ok()) {
      return response.valid();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Verify the signature of the message
  bool valid = subtle_crypto.VerifySignature("ECDSA_SHA_256", public_key,
                                             signature, sign_msg);
  std::cout << "Signature is valid: " << valid << std::endl;

  bool valid_from_wrapped = subtle_crypto.VerifySignature("ECDSA_SHA_256", wrapped_key_ec.public_(),
                                             signature_from_wrapped, sign_with_wrapped_msg);
  std::cout << "Signature from wrapped key is valid: " << valid_from_wrapped << std::endl;
    // Verify the signature of the message
    client.VerifySignature(
      {
        key: resPub.public,
        algorithm: 'ECDSA_SHA_256',
        signature: resSign.signature,
        data: Buffer.from('Message to sign', 'utf8')
      },
      metadata,
      (err, resVerify) => {
        console.log(err || `Signature is valid: ${resVerify.valid}`)
    }
  )
    // Verify the signature of the message made out of wrapped EC key
    client.VerifySignature(
      {
        key: wrappedKeyEcPub,
        algorithm: 'ECDSA_SHA_256',
        signature: resSignWithWrapped.signature,
        data: Buffer.from('Message to sign', 'utf8')
      },
      metadata,
      (err, resVerify) => {
        console.log(err || `Signature with wrapped is valid: ${resVerify.valid}`)
    }
  )
    // Verify the signature of the message
    VerifySignatureRequest reqVerify =
        VerifySignatureRequest.newBuilder()
            .setKey(respPub.getPublic())
            .setAlgorithm("ECDSA_SHA_256")
            .setSignature(respSign.getSignature())
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .build();

    VerifySignatureResponse respVerify = client.verifySignature(reqVerify);

    System.out.println("Signature is valid: " + respVerify.getValid());

    // Verify the signature with wrapped
    VerifySignatureRequest reqVerifyWithWrapped =
        VerifySignatureRequest.newBuilder()
            .setKey(respGenWrapEc.getPublic())
            .setAlgorithm("ECDSA_SHA_256")
            .setSignature(respSignWithWrapped.getSignature())
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .build();

    VerifySignatureResponse respVerifyWithWrapped = client.verifySignature(reqVerifyWithWrapped);

    System.out.println("Signature from wrapped key is valid: " + respVerifyWithWrapped.getValid());

Signature verification is performed given a public key (not a key_id) and a signature. This allows verifying signatures for keys that have not been generated on the platform.

In the given example a signature is checked for the given public key passed in parameter and the string "I want to sign this message using my SECP256K1 key" using ECDSA.

In response a valid field is returned to indicate if the signature passed the validation check.

Encrypting plaintexts

    // Encrypt some payload with AES
    let response = client
        .encrypt(EncryptRequest {
            plaintext: b"I want to encrypt this message using my AES 256 bits key".to_vec(),
            key_id: key_id_aes.clone(),
            algorithm: "AES_CBC_PKCS7".to_string(),
            iv: b"".to_vec(),
        })
        .await?;

    let encrypt_response = response.into_inner();

    println!("Ciper Text: {:?}", &encrypt_response.ciphertext);
    println!("IV: {:?}", &encrypt_response.iv);
    // Encrypt some payload with AES-GCM
    let response = client
        .encrypt_aead(EncryptAeadRequest {
            plaintext: b"I want to encrypt and authenticate this message using my AES 256 bits key"
                .to_vec(),
            aad: b"And this to be authenticated".to_vec(),
            key_id: key_id_aes.clone(),
            algorithm: "AES_GCM".to_string(),
            iv: b"".to_vec(),
        })
        .await?;

    let encrypt_aead_response = response.into_inner();

    println!("Ciper Text: {:?}", &encrypt_aead_response.ciphertext);
    println!("IV: {:?}", &encrypt_aead_response.iv);
    println!("Tag: {:?}", &encrypt_aead_response.authentication_tag);
	// Encrypt some payload with AES
	msg := "I want to encrypt this message using my AES key"
	rsp, err := client.Encrypt(ctx, &crypto.EncryptRequest{
		Plaintext: []byte(msg),
		KeyId:     keyId,
		Algorithm: "AES_CBC_PKCS7",
		Iv:        []byte(""),
	})

	// Check err for errors

	cipherText := rsp.Ciphertext
	fmt.Printf("Cipher Text: %x\n", cipherText)
	iv := rsp.Iv
	fmt.Printf("IV: %x\n", iv)
	// Encrypt some payload with AES
	msg := "I want to encrypt this message using my AES key"
	authMsg := "This data will just be authenticated"
	rsp, err := client.EncryptAead(ctx, &crypto.EncryptAeadRequest{
		Plaintext: []byte(msg),
		KeyId:     keyId,
		Algorithm: "AES_GCM",
		Aad:       []byte(authMsg),
		Iv:        []byte(""),
	})

	// Check err for errors

	cipherText := rsp.Ciphertext
	fmt.Printf("Cipher Text: %x\n", cipherText)
	iv := rsp.Iv
	fmt.Printf("IV: %x\n", iv)
	authTag := rsp.AuthenticationTag
	fmt.Printf("Auth Tag: %x\n", authTag)
    # Encrypt some payload with AES
    msg = 'I want to encrypt this message using my AES key'
    iv = ''
    try:
        req = encrypt_pb2.EncryptRequest(plaintext=msg.encode(),
                                         key_id=key_id_aes,
                                         algorithm='AES_CBC_PKCS7',
                                         iv=iv.encode())
        res = stub.Encrypt(request=req, metadata=metadata)
        encrypt_resp = res
        print('Cipher Text:', encrypt_resp.ciphertext.hex())
        print('IV:', encrypt_resp.iv.hex())
    except Exception as err:
        print('There was an error encrypting the message:', err)
    # Encrypt some payload with AES
    msg = 'I want to encrypt and authenticate this message using my AES key'
    msg_auth = 'And this just to be authenticated'
    iv = ''
    try:
        req = encrypt_pb2.EncryptAeadRequest(plaintext=msg.encode(),
                                             aad=msg_auth.encode(),
                                             key_id=key_id_aes,
                                             algorithm='AES_GCM',
                                             iv=iv.encode())
        res = stub.EncryptAead(request=req, metadata=metadata)
        encrypt_resp_gcm = res
        print('Cipher Text:', encrypt_resp_gcm.ciphertext.hex())
        print('IV:', encrypt_resp_gcm.iv.hex())
        print('Auth tag:', encrypt_resp_gcm.authentication_tag.hex())
    except Exception as err:
        print('There was an error encrypting the message:', err)
      // Encrypt some payload with AES
      val msg: String = "I want to encrypt this message using my AES 256 key"
      rspEnc <- client.encrypt(
        EncryptRequest(
          ByteString.copyFromUtf8(msg),
          rspGenAes.keyId,
          "AES_CBC_PKCS7",
          ByteString.EMPTY
        )
      )
      _ = println(
        "Cipher Text: " + rspEnc.ciphertext
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
      _ = println(
        "IV: " + rspEnc.iv
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
      // Encrypt some payload with AES-GCM
      val msgGcm: String =
        "I want to encrypt and authenticate this message using my AES 256 key"
      val msg_auth: String = "And this to be authenticated"
      rspEncGcm <- client.encryptAead(
        EncryptAeadRequest(
          ByteString.copyFromUtf8(msgGcm),
          rspGenAes.keyId,
          "AES_GCM",
          ByteString.EMPTY,
          ByteString.copyFromUtf8(msg_auth)
        )
      )
      _ = println(
        "Cipher Text: " + rspEncGcm.ciphertext
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
      _ = println(
        "IV: " + rspEncGcm.iv
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )

      _ = println(
        "Tag: " + rspEncGcm.authenticationTag
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
    var msgAes = "I want to encrypt this message using my AES key";
    var replyEncrypt = client.Encrypt(
        new EncryptRequest { Algorithm = "AES_CBC_PKCS7", KeyId = keyIdAes,
                             Plaintext = ByteString.CopyFromUtf8(msgAes),
                             Iv = ByteString.Empty },
        metadata);

    Console.WriteLine("Cipher Text: " + Bytes2Hex(replyEncrypt.Ciphertext));
    Console.WriteLine("IV: " + Bytes2Hex(replyEncrypt.Iv));
    var msgAesGcm =
        "I want to encrypt and authenticate this message using my AES key";
    var authenticatedData = "This message will be authenticated";
    var replyEncryptGcm = client.EncryptAead(
        new EncryptAeadRequest { Algorithm = "AES_GCM", KeyId = keyIdAes,
                                 Plaintext = ByteString.CopyFromUtf8(msgAesGcm),
                                 Aad =
                                     ByteString.CopyFromUtf8(authenticatedData),
                                 Iv = ByteString.Empty },
        metadata);

    Console.WriteLine("Cipher Text: " + Bytes2Hex(replyEncryptGcm.Ciphertext));
    Console.WriteLine("IV: " + Bytes2Hex(replyEncryptGcm.Iv));
    Console.WriteLine("Authentication Tag: " +
                      Bytes2Hex(replyEncryptGcm.AuthenticationTag));
  // Inside the public part of class SubtleCryptoClient
  crypto::EncryptResponse Encrypt(const std::string &algorithm,
                                  const std::string &key_id,
                                  const std::string &data,
                                  const std::string &iv) {
    crypto::EncryptRequest request;
    request.set_algorithm(algorithm);
    request.set_key_id(key_id);
    request.set_plaintext(data);
    // Can be empty
    request.set_iv(iv);

    crypto::EncryptResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->Encrypt(&context, request, &response);

    if (status.ok()) {
      return response;
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Encrypt some payload with AES
  std::string encrypt_msg("I want to encrypt this message using my AES key");
  // The last parameter is the IV and can be empty to be generated by the
  // backend
  crypto::EncryptResponse encrypt_response =
      subtle_crypto.Encrypt("AES_CBC_PKCS7", key_id_aes, encrypt_msg, "");
  print_bytes("Cipher Text: : ", encrypt_response.ciphertext());
  print_bytes("IV used: : ", encrypt_response.iv());
  // Inside the public part of class SubtleCryptoClient
  crypto::EncryptAeadResponse EncryptAead(const std::string &algorithm,
                                          const std::string &key_id,
                                          const std::string &data,
                                          const std::string &authenticated_data,
                                          const std::string &iv) {
    crypto::EncryptAeadRequest request;
    request.set_algorithm(algorithm);
    request.set_key_id(key_id);
    request.set_plaintext(data);
    request.set_aad(authenticated_data);
    // Can be empty
    request.set_iv(iv);

    crypto::EncryptAeadResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->EncryptAead(&context, request, &response);

    if (status.ok()) {
      return response;
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Encrypt some payload with AES-GCM
  std::string auth_encrypted_msg(
      "I want to encrypt this message using my AES key");
  std::string auth_msg("And this will just be authentified !");

  // The last parameter is the IV and can be empty to be generated by the
  // backend
  crypto::EncryptAeadResponse encrypted_auth_response =
      subtle_crypto.EncryptAead("AES_GCM", key_id_aes, auth_encrypted_msg,
                                auth_msg, "");
  print_bytes("Cipher Text: : ", encrypted_auth_response.ciphertext());
  print_bytes("IV used: : ", encrypted_auth_response.iv());
  print_bytes("Auth TAG: : ", encrypted_auth_response.authentication_tag());
    client.Encrypt(
      {
        algorithm: 'AES_CBC_PKCS7',
        key_id: keyIdAes,
        plaintext: Buffer.from('I want to encrypt this message using my AES key', 'utf8'),
        iv: Buffer.from('', 'utf8')
      },
      metadata,
      (err, resEncrypt) => {
        console.log(err || `Cipher Text: ${resEncrypt.ciphertext.toString('hex')}`)
        console.log(err || `IV: ${resEncrypt.iv.toString('hex')}`)
    }
  )
    client.EncryptAead(
      {
        algorithm: 'AES_GCM',
        key_id: keyIdAes,
        plaintext: Buffer.from('I want to encrypt and authenticate this message using my AES key', 'utf8'),
        aad: Buffer.from('And this to be authenticated', 'utf-8'),
        iv: Buffer.from('', 'utf8')
      },
      metadata,
      (err, resEncrypt) => {
        console.log(err || `Cipher Text: ${resEncrypt.ciphertext.toString('hex')}`)
        console.log(err || `IV: ${resEncrypt.iv.toString('hex')}`)
        console.log(err || `Tag: ${resEncrypt.authentication_tag.toString('hex')}`)
    }
  )
    // Encrypt some payload with AES
    EncryptRequest reqEncrypt =
        EncryptRequest.newBuilder()
            .setKeyId(respGenAes.getKeyId())
            .setAlgorithm("AES_CBC_PKCS7")
            .setPlaintext(ByteString.copyFromUtf8("Message to encrypt"))
            .setIv(ByteString.EMPTY)
            .build();

    EncryptResponse respEncrypt = client.encrypt(reqEncrypt);

    System.out.println("Cipher Text: " + respEncrypt.getCiphertext());
    System.out.println("IV: " + respEncrypt.getIv());
    // Encrypt some payload with AES-GCM
    EncryptAeadRequest reqEncryptAead =
        EncryptAeadRequest.newBuilder()
            .setKeyId(respGenAes.getKeyId())
            .setAlgorithm("AES_GCM")
            .setPlaintext(
                ByteString.copyFromUtf8("Message to encrypt and authenticate"))
            .setAad(
                ByteString.copyFromUtf8("And this will be authenticated only"))
            .setIv(ByteString.EMPTY)
            .build();

    EncryptAeadResponse respEncryptAead = client.encryptAead(reqEncryptAead);

    System.out.println("Cipher Text: " + respEncryptAead.getCiphertext());
    System.out.println("IV: " + respEncryptAead.getIv());
    System.out.println("Tag: " + respEncryptAead.getAuthenticationTag());

Data encryption is done on a provided plaintext with a key_id and an algorithm. The algorithm contains the type of the cipher and, in most cases, a padding scheme or block cipher mode.

There are multiple RPC calls for encryption. For instance, symmetric encryption algorithms are used through the Encrypt RPC and public encryption algorithms (such as RSA) use the PublicEncrypt RPC.

Authenticated encryption algorithms use their own RPC call (EncryptAead) and some hybrid schemes such as ECIES also have their own call to avoid overloading messages for simple encryption operations.

Decrypting ciphertexts

    let response = client
        .decrypt(DecryptRequest {
            ciphertext: encrypt_response.ciphertext,
            key_id: key_id_aes.clone(),
            iv: encrypt_response.iv,
            algorithm: "AES_CBC_PKCS7".to_string(),
        })
        .await?;

    println!(
        "Plain Text: {:?}",
        str::from_utf8(&response.into_inner().plaintext[..]).unwrap()
    );
    // Decrypt some payload with AES-GCM
    let response = client
        .decrypt_aead(DecryptAeadRequest {
            ciphertext: encrypt_aead_response.ciphertext,
            aad: b"And this to be authenticated".to_vec(),
            authentication_tag: encrypt_aead_response.authentication_tag,
            key_id: key_id_aes.clone(),
            iv: encrypt_aead_response.iv,
            algorithm: "AES_GCM".to_string(),
        })
        .await?;

    println!(
        "Plain Text: {:?}",
        str::from_utf8(&response.into_inner().plaintext[..]).unwrap()
    );
	// Decrypt the payload with AES
	rsp, err := client.Decrypt(ctx, &crypto.DecryptRequest{
		Ciphertext: cipherText,
		KeyId:      keyId,
		Algorithm:  "AES_CBC_PKCS7",
		Iv:         iv,
	})

	// Check err for errors

	plainText := rsp.Plaintext
	fmt.Printf("Plain Text: %s\n", plainText)
	// Decrypt the payload with AES
	rsp, err := client.DecryptAead(ctx, &crypto.DecryptAeadRequest{
		Ciphertext:        cipherText,
		Aad:               []byte(authMsg),
		AuthenticationTag: authTag,
		KeyId:             keyId,
		Algorithm:         "AES_GCM",
		Iv:                iv,
	})

	// Check err for errors

	plainText := rsp.Plaintext
	fmt.Printf("Plain Text: %s\n", plainText)
    # Decrypt the payload with AES
    try:
        req = encrypt_pb2.DecryptRequest(ciphertext=encrypt_resp.ciphertext,
                                         key_id=key_id_aes,
                                         algorithm='AES_CBC_PKCS7',
                                         iv=encrypt_resp.iv)
        res = stub.Decrypt(request=req, metadata=metadata)
        print('Plain Text:', res.plaintext)
    except Exception as err:
        print('There was an error decrypting the message:', err)
    msg_auth = 'And this just to be authenticated'
    # Decrypt the payload with AES_GCM
    try:
        req = encrypt_pb2.DecryptAeadRequest(
            ciphertext=encrypt_resp_gcm.ciphertext,
            aad=msg_auth.encode(),
            authentication_tag=encrypt_resp_gcm.authentication_tag,
            key_id=key_id_aes,
            algorithm='AES_GCM',
            iv=encrypt_resp_gcm.iv)
        res = stub.DecryptAead(request=req, metadata=metadata)
        print('Plain Text:', res.plaintext)
    except Exception as err:
        print('There was an error decrypting the message:', err)
      rspDec <- client.decrypt(
        DecryptRequest(
          rspEnc.ciphertext,
          rspGenAes.keyId,
          "AES_CBC_PKCS7",
          rspEnc.iv
        )
      )
      _ = println(
        "Clear Text: " + rspDec.plaintext.toStringUtf8()
      )
      rspDecGcm <- client.decryptAead(
        DecryptAeadRequest(
          rspEncGcm.ciphertext,
          rspGenAes.keyId,
          "AES_GCM",
          rspEncGcm.iv,
          rspEncGcm.authenticationTag,
          ByteString.copyFromUtf8(msg_auth)
        )
      )
      _ = println(
        "Clear Text: " + rspDecGcm.plaintext.toStringUtf8()
      )
    var replyDecrypt = client.Decrypt(
        new DecryptRequest { Algorithm = "AES_CBC_PKCS7", KeyId = keyIdAes,
                             Ciphertext = replyEncrypt.Ciphertext,
                             Iv = replyEncrypt.Iv },
        metadata);

    Console.WriteLine("Clear Text: " + replyDecrypt.Plaintext.ToStringUtf8());
    var replyDecryptGcm = client.DecryptAead(
        new DecryptAeadRequest { Algorithm = "AES_GCM", KeyId = keyIdAes,
                                 Ciphertext = replyEncryptGcm.Ciphertext,
                                 AuthenticationTag =
                                     replyEncryptGcm.AuthenticationTag,
                                 Aad =
                                     ByteString.CopyFromUtf8(authenticatedData),
                                 Iv = replyEncryptGcm.Iv },
        metadata);

    Console.WriteLine("Clear Text: " +
                      replyDecryptGcm.Plaintext.ToStringUtf8());
  // Inside the public part of class SubtleCryptoClient
  std::string Decrypt(const std::string &algorithm, const std::string &key_id,
                      const std::string &ciphertext, const std::string &iv) {
    crypto::DecryptRequest request;
    request.set_algorithm(algorithm);
    request.set_key_id(key_id);
    request.set_ciphertext(ciphertext);
    request.set_iv(iv);

    crypto::DecryptResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->Decrypt(&context, request, &response);

    if (status.ok()) {
      return response.plaintext();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Decrypt the payload with AES
  std::string plaintext = subtle_crypto.Decrypt("AES_CBC_PKCS7", key_id_aes,
                                                encrypt_response.ciphertext(),
                                                encrypt_response.iv());
  std::cout << "Plain Text: : " << plaintext << std::endl;
  // Inside the public part of class SubtleCryptoClient
  std::string
  DecryptAead(const std::string &algorithm, const std::string &key_id,
              const std::string &ciphertext, const std::string &auth_tag,
              const std::string &authenticated_data, const std::string &iv) {
    crypto::DecryptAeadRequest request;
    request.set_algorithm(algorithm);
    request.set_key_id(key_id);
    request.set_ciphertext(ciphertext);
    request.set_iv(iv);
    request.set_aad(authenticated_data);
    request.set_authentication_tag(auth_tag);

    crypto::DecryptAeadResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->DecryptAead(&context, request, &response);

    if (status.ok()) {
      return response.plaintext();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Decrypt the payload with AES
  std::string plaintext_auth = subtle_crypto.DecryptAead(
      "AES_GCM", key_id_aes, encrypted_auth_response.ciphertext(),
      encrypted_auth_response.authentication_tag(), auth_msg,
      encrypted_auth_response.iv());
  std::cout << "Plain Text: : " << plaintext_auth << std::endl;
    // Decrypt the payload with AES
    client.Decrypt(
      {
        algorithm: 'AES_CBC_PKCS7',
        key_id: keyIdAes,
        ciphertext: resEncrypt.ciphertext,
        iv: resEncrypt.iv
      },
      metadata,
      (err, resDecrypt) => {
        console.log(err || `Plain text: ${resDecrypt.plaintext}`)
    }
  )
    // Decrypt the payload with AES-GCM
    client.DecryptAead(
      {
        algorithm: 'AES_GCM',
        key_id: keyIdAes,
        ciphertext: resEncrypt.ciphertext,
        aad: Buffer.from('And this to be authenticated', 'utf-8'),
        authentication_tag: resEncrypt.authentication_tag,
        iv: resEncrypt.iv
      },
      metadata,
      (err, resDecrypt) => {
        console.log(err || `Plain text: ${resDecrypt.plaintext}`)
    }
  )
    // Decrypt the payload with AES
    DecryptRequest reqDecrypt = DecryptRequest.newBuilder()
                                    .setKeyId(respGenAes.getKeyId())
                                    .setAlgorithm("AES_CBC_PKCS7")
                                    .setCiphertext(respEncrypt.getCiphertext())
                                    .setIv(respEncrypt.getIv())
                                    .build();

    DecryptResponse respDecrypt = client.decrypt(reqDecrypt);

    System.out.println("Plain Text: " +
                       respDecrypt.getPlaintext().toStringUtf8());
    // Decrypt the payload with AES_GCM
    DecryptAeadRequest reqDecryptAead =
        DecryptAeadRequest.newBuilder()
            .setKeyId(respGenAes.getKeyId())
            .setAlgorithm("AES_GCM")
            .setCiphertext(respEncryptAead.getCiphertext())
            .setAad(
                ByteString.copyFromUtf8("And this will be authenticated only"))
            .setAuthenticationTag(respEncryptAead.getAuthenticationTag())
            .setIv(respEncryptAead.getIv())
            .build();

    DecryptAeadResponse respDecryptAead = client.decryptAead(reqDecryptAead);

    System.out.println("Plain Text: " +
                       respDecryptAead.getPlaintext().toStringUtf8());

Data decryption is done on a provided ciphertext with a key_id and an algorithm. Like in the encryption, the algorithm contains the type of cipher and, in most cases, a padding scheme or block cipher mode.

In response the plaintext is returned if the decryption succeeded.

Derive with ECDH

    //Generate Bob key (other side)
    let response = client
        .generate_key(GenerateKeyRequest {
            algorithm: "EC_SECP256K1".to_string(),
            whitelist: vec!["ECDH".to_string()],
            delete_after: String::new(),
        })
        .await?;
    let bob_key_id = response.into_inner().key_id;
    let response = client
    .get_public_component(GetPublicComponentRequest {
        key_id: bob_key_id.clone(),
    })
    .await?;
    let bob_public_key = response.into_inner().public;
    //Derive share secret with ECDH
    let response = client
        .derive_ecdh(DeriveEcdhRequest{
            key_id: key_id_ec,
            public_key: bob_public_key.clone()
        }).await?;
    let response = response.into_inner();

    println!("Share secret: {:?}", response.shared_secret);
    //Derive share secret with ECDH
    let response = client
    .derive_ecdh_with_wrapped(DeriveEcdhWithWrappedRequest{
        wrapped_secret: wrapped_key_ec,
        public_key: bob_public_key.clone() 
    }).await?;
    let response = response.into_inner();

    println!("Share secret: {:?}", response.shared_secret);
	//Generate Bob key (other side)
	rspBobKey, errBobKey := client.GenerateKey(ctx, &crypto.GenerateKeyRequest{
		Algorithm: "EC_SECP256K1",
		Whitelist: []string{"ECDH"},
	})

	// Check err for errors

	bobKeyId := rspBobKey.KeyId
	fmt.Printf("Bob key id: %s\n", bobKeyId)

	rspBobPublic, errBobPublic := client.GetPublicComponent(ctx, &crypto.GetPublicComponentRequest{
		KeyId: bobKeyId,
	})

	// Check err for errors

	bobPublicKey := rspBobPublic.Public
	fmt.Printf("Bob Public key: %x\n", bobPublicKey)
	//Derive share secret with ECDH
	rsp, err := client.DeriveEcdh(ctx, &crypto.DeriveEcdhRequest{
		KeyId:     keyId,
		PublicKey: bobPublicKey,
	})

	// Check err for errors

	sharedSecret := rsp.SharedSecret
	fmt.Printf("Shared secret: %x\n", sharedSecret)
	//Derive share secret with ECDH
	rsp, err := client.DeriveEcdhWithWrapped(ctx, &crypto.DeriveEcdhWithWrappedRequest{
		WrappedSecret: wrappedKey,
		PublicKey:     bobPublicKey,
	})

	// Check err for errors

	sharedSecret := rsp.SharedSecret
	fmt.Printf("Shared secret: %x\n", sharedSecret)
    # Generate bob key (other side)
    try:
        req = generate_pb2.GenerateKeyRequest(algorithm='EC_SECP256K1',
                                                whitelist=['ECDH'])
        res = stub.GenerateKey(request=req, metadata=metadata)
        req = public_pb2.GetPublicComponentRequest(key_id=res.key_id)
        res = stub.GetPublicComponent(request=req, metadata=metadata)
        bob_public_key = res.public
    except Exception as err:
        print('Unable to generate Bob\'s key:', err)
    # Derive share secret with ECDH
    try:
        req = key_exchange_pb2.DeriveEcdhRequest(
            key_id=key_id_ec,
            public_key=bob_public_key)
        res = stub.DeriveEcdh(request=req, metadata=metadata)
        print('Shared Secret:', res.shared_secret)
    except Exception as err:
        print('There was an error deriving shared secret:', err)
    # Derive share secret with ECDH
    try:
        req = key_exchange_pb2.DeriveEcdhWithWrappedRequest(
            wrapped_secret=wrapped_key_ec,
            public_key=bob_public_key)
        res = stub.DeriveEcdhWithWrapped(request=req, metadata=metadata)
        print('Shared Secret:', res.shared_secret)
    except Exception as err:
        print('There was an error deriving shared secret:', err)
      // Generate Bob key (other side)
      rspBobKey <-
        client
          .generateKey(
            GenerateKeyRequest(
              "EC_SECP256K1",
              Seq("ECDSA_SHA_256")
            )
          )
      _ = println("Bob Key id: " + rspBobKey.keyId)

      rspBobPublicKey <-
        client
          .getPublicComponent(
            GetPublicComponentRequest(
              rspBobKey.keyId
            )
          )
      _ = println(
            "Bob Public Key: " + rspBobPublicKey.public)
      //Derive share secret with ECDH
      rspDeriveEcdh <-
        client
          .deriveEcdh(
            DeriveEcdhRequest(
              rspGenEc.keyId,
              rspBobPublicKey.public
            )
          )
      _ = println(
            "Shared Secret: " + rspDeriveEcdh.sharedSecret
              .toByteArray()
              .map("%02x" format _)
              .mkString
          )
      //Derive share secret with ECDH
      rspDeriveEcdhWithWrapped <-
        client
          .deriveEcdhWithWrapped(
            DeriveEcdhWithWrappedRequest(
              rspGenWrappedEc.wrappedSecret,
              rspBobPublicKey.public
            )
          )
      _ = println(
            "Shared Secret: " + rspDeriveEcdhWithWrapped.sharedSecret
              .toByteArray()
              .map("%02x" format _)
              .mkString
          )
    //Generate Bob key (other side)
    var reqBobKey = new GenerateKeyRequest { Algorithm = "EC_SECP256K1",
                                            Whitelist = { "ECDH" } };

    var bobKeyId = client.GenerateKey(reqBobKey, metadata).KeyId;
    Console.WriteLine("Bob Key ID: " + bobKeyId);

    var bobPublicKey =
        client
            .GetPublicComponent(
                new GetPublicComponentRequest { KeyId = bobKeyId }, metadata)
            .Public;

    Console.WriteLine("Bob Public Key: " + Bytes2Hex(bobPublicKey));
    //Derive share secret with ECDH
    var sharedSecret = client.DeriveEcdh(
        new DeriveEcdhRequest {
            KeyId = keyIdEc,
            PublicKey = bobPublicKey
        },
        metadata
    ).SharedSecret;

    Console.WriteLine("Shared Secret: " + Bytes2Hex(sharedSecret));
    //Derive share secret with ECDH
    var sharedSecretFromWrapped = client.DeriveEcdhWithWrapped(
        new DeriveEcdhWithWrappedRequest {
            WrappedSecret = respGenWrappedEc.WrappedSecret,
            PublicKey = bobPublicKey
        },
        metadata
    ).SharedSecret;

    Console.WriteLine("Shared Secret: " + Bytes2Hex(sharedSecretFromWrapped));
  //Generate Bob key (other side)
  std::vector<std::string> whitelist_bob;
  whitelist_bob.push_back("ECDH");
  std::string bob_key_id =
      subtle_crypto.GenerateKey("EC_SECP256K1", whitelist_bob);
  std::cout << "EC Key id: " << bob_key_id << std::endl;

  std::string bob_public_key = subtle_crypto.GetPublicComponent(bob_key_id);
  print_bytes("Public Key: ", bob_public_key);
  std::string
  DeriveEcdh(const std::string &key_id, const std::string &bob_public_key) {
    crypto::DeriveEcdhRequest request;
    request.set_key_id(key_id);
    request.set_public_key(bob_public_key);

    crypto::DeriveEcdhResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->DeriveEcdh(&context, request, &response);

    if (status.ok()) {
      return response.shared_secret();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  //Derive share secret with ECDH
  std::string shared_secret =
      subtle_crypto.DeriveEcdh(key_id_ec, bob_public_key);
  print_bytes("Shared Secret: ", shared_secret);
  std::string
  DeriveEcdhWithWrapped(const std::string &wrapped_key, const std::string &bob_public_key) {
    crypto::DeriveEcdhWithWrappedRequest request;
    request.set_wrapped_secret(wrapped_key);
    request.set_public_key(bob_public_key);

    crypto::DeriveEcdhResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->DeriveEcdhWithWrapped(&context, request, &response);

    if (status.ok()) {
      return response.shared_secret();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  //Derive share secret with ECDH
  std::string shared_secret_from_wrapped =
      subtle_crypto.DeriveEcdhWithWrapped(wrapped_key_ec.wrapped_secret(), bob_public_key);
  print_bytes("Shared Secret: ", shared_secret_from_wrapped);
    // Generate Bob keys (other side)
    client.GenerateKey(
      {
        algorithm: 'EC_SECP256K1',
        whitelist: [
          'ECDH'
        ]
      },
      metadata,
      (err, res) => {
        const bobKeyId = res.key_id
        console.log(err || `Bob key id ${bobKeyId}`)
    }
  )

    client.GetPublicComponent(
      {
        key_id: bobKeyId
      },
      metadata,
      (err, resPub) => {
        console.log(err || `Bob Public key: ${resPub.public.toString('hex')}`)
    }
  )
    //Derive share secret with ECDH
    client.DeriveEcdh(
      {
        key_id: keyIdEc,
        public_key: bobPublicKey.public
      },
      metadata,
      (err, resEcdh) => {
        console.log(err || `Share secret: ${resEcdh.shared_secret}`)
    }
  )
    //Derive share secret with ECDH
    client.DeriveEcdhWithWrapped(
      {
        wrapped_secret: wrappedKeyEc,
        public_key: bobPublicKey.public
      },
      metadata,
      (err, resEcdh) => {
        console.log(err || `Share secret: ${resEcdh.shared_secret}`)
    }
  )
    // Generate Bob keys (other side)
    GenerateKeyRequest reqGenBobEc =
        GenerateKeyRequest.newBuilder()
            .setAlgorithm("EC_SECP256K1")
            .addWhitelist("ECDH")
            .build();
    GenerateKeyResponse respGenBobEc = client.generateKey(reqGenBobEc);
    GetPublicComponentRequest reqBobPub =
        GetPublicComponentRequest.newBuilder()
            .setKeyId(respGenBobEc.getKeyId())
            .build();
    GetPublicComponentResponse respBobPub = client.getPublicComponent(reqBobPub);
    ByteString bobPublicKey = respBobPub.getPublic();
    //Derive share secret with ECDH
    DeriveEcdhRequest reqEcdh =
        DeriveEcdhRequest.newBuilder()
            .setKeyId(respGenEc.getKeyId())
            .setPublicKey(bobPublicKey)
            .build();
    DeriveEcdhResponse respEcdh = client.deriveEcdh(reqEcdh);
    System.out.println("Shared Secret: " + respEcdh.getSharedSecret());
    //Derive share secret with ECDH
    DeriveEcdhWithWrappedRequest reqEcdhWithWrapped =
        DeriveEcdhWithWrappedRequest.newBuilder()
            .setWrappedSecret(respGenWrapEc.getWrappedSecret())
            .setPublicKey(bobPublicKey)
            .build();
    DeriveEcdhResponse respEcdhWithWrapped = client.deriveEcdhWithWrapped(reqEcdhWithWrapped);
    System.out.println("Shared Secret: " + respEcdhWithWrapped.getSharedSecret());

ECDH derives a shared secret from a private key and a public key. This operation is deterministic and will always return the same secret for the same key pair. The opposite party must do the reverse operation with their private key and your public key to derive the same secret.

The deterministic limitation of this algorithm advises the use of ephemeral keys to avoid excessive reuse of the same shared secret. The version using wrapped keys is ideal for implementing an ECDH with ephemeral keys.

Wallet key derivation

    let response = client
        .generate_wallet_seed(GenerateWalletSeedRequest { length: 32 })
        .await?;

    let res_seed = response.into_inner();

    let response = client
        .derive_wallet_master_key(DeriveWalletMasterKeyRequest {
            algorithm: "BIP32".to_string(),
            whitelist: vec!["ECDSA_SHA_512".to_string()],
            seed: Some(derive_wallet_master_key_request::Seed::Wrapped(res_seed.wrapped_seed)),
        })
        .await?;

    // It is also possible to use a plaintext seed
    //
    // let response = client
    //     .get_entropy(GetEntropyRequest {
    //         length: 64,
    //         source: "PRNG".to_string(),
    //     })
    //     .await?;
    //
    // let res_seed = response.into_inner();
    //
    // ...
    // seed: Some(Seed::Plaintext(res_seed.entropy)),
    // ...

    let res_master = response.into_inner();

    println!("Master key id: {:?}", &res_master.key_id);
    let response = client
        .get_wallet_public_components(GetWalletPublicComponentsRequest {
            key_id: res_master.key_id.clone(),
        })
        .await?;

    let res_public_master = response.into_inner();
    println!("Master Public key {:?}", &res_public_master.public);
    println!("Master Chaincode : {:?}", &res_public_master.chain_code);
    println!(
        "Master Fingerprint : {:?}",
        &res_public_master.parent_fingerprint
    );
    println!("Master Index key : {:?}", &res_public_master.index);
    println!("Depth of master key : {:?}", &res_public_master.depth);
    let response = client
        .derive_wallet_private_key(DeriveWalletPrivateKeyRequest {
            key_id: res_master.key_id.clone(),
            whitelist: vec!["ECDSA_SHA_512".to_string()],
            path: vec![2, 4, 1],
        })
        .await?;

    let res_private = response.into_inner();
    println!("Private key id: {:?}", &res_private.key_id);

    let response = client
        .sign(SignRequest {
            data: b"I want to sign this with wallet private key".to_vec(),
            key_id: res_private.key_id.clone(),
            algorithm: "ECDSA_SHA_512".to_string(),
        })
        .await?;
    let res_sign_wallet = response.into_inner();
    println!("Signature : {:?}", &res_sign_wallet.signature);
    let response = client
        .derive_wallet_public_key(DeriveWalletPublicKeyRequest {
            algorithm: "BIP32".to_string(),
            chain_code: res_public_master.chain_code,
            public_key: res_public_master.public,
            path: vec![2, 4, 1],
        })
        .await?;
    let res_public_wallet = response.into_inner();
    println!("Public key {:?}", &res_public_wallet.public_key);
    println!("Chaincode : {:?}", &res_public_wallet.chain_code);

    let response = client
        .verify_signature(VerifySignatureRequest {
            data: b"I want to sign this with wallet private key".to_vec(),
            signature: res_sign_wallet.signature,
            key: res_public_wallet.public_key,
            algorithm: "ECDSA_SHA_512".to_string(),
        })
        .await?;

    println!("Signature is valid: {:?}", response.into_inner().valid);
    let response = client
    .generate_wallet_seed(GenerateWalletSeedRequest { length: 32 })
    .await?;

    let res_seed = response.into_inner();

    let response = client
    .derive_wallet_public_key_one_shot(DeriveWalletPublicKeyOneShotRequest {
        algorithm: "BIP32".to_string(),
        seed: Some(derive_wallet_public_key_one_shot_request::Seed::Wrapped(res_seed.wrapped_seed.clone())),
        path: vec![4, 8, 2],
    })
    .await?;
    let res_public_oneshot_wallet = response.into_inner();
    println!("Public key {:?}", &res_public_oneshot_wallet.public_key);
    println!("Chaincode : {:?}", &res_public_oneshot_wallet.chain_code);
    let response = client
    .derive_wallet_and_sign_one_shot(DeriveWalletAndSignOneShotRequest {
        algorithm: "BIP32".to_string(),
        seed: Some(derive_wallet_and_sign_one_shot_request::Seed::Wrapped(res_seed.wrapped_seed)),
        path: vec![4, 8, 2],
        data: b"I want to sign this with `one shot calls`".to_vec(),
        signature_algorithm: "ECDSA_SHA_512".to_string(),
    })
    .await?;
    let res_sign_oneshot_wallet = response.into_inner();

    let response = client
        .verify_signature(VerifySignatureRequest {
            data: b"I want to sign this with `one shot calls`".to_vec(),
            signature: res_sign_oneshot_wallet.signature,
            key: res_public_oneshot_wallet.public_key,
            algorithm: "ECDSA_SHA_512".to_string(),
        })
        .await?;

    println!("Signature is valid: {:?}", response.into_inner().valid);
	rsp, err := client.GenerateWalletSeed(ctx, &crypto.GenerateWalletSeedRequest{
		Length: 32,
	})

	// Check for errors

	rspMaster, errMaster := client.DeriveWalletMasterKey(ctx, &crypto.DeriveWalletMasterKeyRequest{
		Algorithm: "BIP32",
		Whitelist: []string{"ECDSA_SHA_512"},
		Seed: &crypto.DeriveWalletMasterKeyRequest_Wrapped{
			Wrapped: rsp.WrappedSeed,
		},
	})

	// It is also possible to use a plaintext seed
	//
	// rsp, err := client.GetEntropy(ctx, &crypto.GetEntropyRequest{
	// 	Source: "PRNG",
	// 	Length: 64,
	// })
	//
	// ...
	// Seed: &crypto.DeriveWalletMasterKeyRequest_Wrapped{
	//     Plaintext: rsp.Entropy,
	// },
	// ...

	// Check for errors

	masterKey := rspMaster.KeyId
	fmt.Printf("Master Key: %s\n", masterKey)
	rsp, err := client.GetWalletPublicComponents(ctx, &crypto.GetWalletPublicComponentsRequest{
		KeyId: keyIdWalletMaster,
	})

	publicKey := rsp.Public
	fmt.Printf("Master Public key: %x\n", publicKey)
	chainCode := rsp.ChainCode
	fmt.Printf("Master ChainCode: %x\n", chainCode)
	fingerprint := rsp.ParentFingerprint
	fmt.Printf("Master Fingerprint: %x\n", fingerprint)
	index := rsp.Index
	fmt.Printf("Index : %x\n", index)
	depth := rsp.Depth
	fmt.Printf("Depth: %x\n", depth)
	rsp, err := client.DeriveWalletPrivateKey(ctx, &crypto.DeriveWalletPrivateKeyRequest{
		KeyId:     keyIdWalletMaster,
		Whitelist: []string{"ECDSA_SHA_512"},
		Path:      []uint32{2, 4, 1},
	})

	// Check for errors

	privateKey := rsp.KeyId
	fmt.Printf("Private KeyId: %s\n", privateKey)
	msg := "I want to sign this"
	rspSign, errSign := client.Sign(ctx, &crypto.SignRequest{
		Data:      []byte(msg),
		Algorithm: "ECDSA_SHA_512",
		KeyId:     privateKey,
	})

	// Check for errors

	signature := rspSign.Signature
	fmt.Printf("Signature: %x\n", signature)
	rsp, err := client.DeriveWalletPublicKey(ctx, &crypto.DeriveWalletPublicKeyRequest{
		Algorithm: "BIP32",
		PublicKey: pubKey,
		ChainCode: chainCode,
		Path:      []uint32{2, 4, 1},
	})

	// Check for errors

	publicKey := rsp.PublicKey
	fmt.Printf("Public key m/2/4/1 : %x\n", publicKey)
	chainCodeRecv := rsp.ChainCode
	fmt.Printf("ChainCode m/2/4/1: %x\n", chainCodeRecv)

	rspSign, errSign := client.VerifySignature(ctx, &crypto.VerifySignatureRequest{
		Algorithm: "ECDSA_SHA_512",
		Key:       publicKey,
		Signature: signature,
		Data:      []byte(msg),
	})

	// Check err for errors

	valid := rspSign.Valid
	fmt.Printf("Signature is valid: %t\n", valid)

	rspPublicKey, errMaster := client.DeriveWalletPublicKeyOneShot(ctx, &crypto.DeriveWalletPublicKeyOneShotRequest{
		Algorithm: "BIP32",
		Seed: &crypto.DeriveWalletPublicKeyOneShotRequest_Wrapped{
			Wrapped: seed,
		},
		Path: []uint32{4, 8, 2},
	})

	// Check for errors

	publicKey := rspPublicKey.PublicKey
	chainCode := rspPublicKey.ChainCode
	fmt.Printf("Public Key: %x\n", publicKey)
	fmt.Printf("Chain Code: %x\n", chainCode)
	msg := "I want to sign this message using my SECP256K1 key"
	rspSign, errSign := client.DeriveWalletAndSignOneShot(ctx, &crypto.DeriveWalletAndSignOneShotRequest{
		Algorithm: "BIP32",
		Seed: &crypto.DeriveWalletAndSignOneShotRequest_Wrapped{
			Wrapped: seed,
		},
		Path:               []uint32{4, 8, 2},
		SignatureAlgorithm: "ECDSA_SHA_512",
		Data:               []byte(msg),
	})

	// Check for errors

	signature := rspSign.Signature
	fmt.Printf("Signature: %x\n", signature)

	//Public key from WalletDerivePublicKeyOneShot
	rspVerify, errVerify := client.VerifySignature(ctx, &crypto.VerifySignatureRequest{
		Algorithm: "ECDSA_SHA_512",
		Key:       publicKey,
		Signature: signature,
		Data:      []byte(msg),
	})

	// Check for errors

	valid := rspVerify.Valid
	fmt.Printf("Signature is valid: %t\n", valid)
    try:
        reqSeed = wallet_pb2.GenerateWalletSeedRequest(length=32)
        resSeed = stub.GenerateWalletSeed(request=reqSeed, metadata=metadata)
    except Exception as err:
        print('There was error getting entropy : ', err)
    try:
        reqMaster = wallet_pb2.DeriveWalletMasterKeyRequest(
            algorithm='BIP32',
            whitelist=['ECDSA_SHA_512'],
            wrapped=resSeed.wrapped_seed)

        # It is also possible to use a plaintext seed
        #
        # reqSeed = rand_pb2.GetEntropyRequest(length=64, source='PRNG')
        # resSeed = stub.GetEntropy(request=reqSeed, metadata=metadata)
        #
        # ...
        # plaintext = resSeed.entropy,
        # ...

        resMaster = stub.DeriveWalletMasterKey(request=reqMaster,
                                               metadata=metadata)
        print('Master key id : ', resMaster.key_id)
    except Exception as err:
        print('Error deriving master wallet key : ', err)
    try:
        reqMasterPublic = wallet_pb2.GetWalletPublicComponentsRequest(
            key_id=resMaster.key_id)
        resMasterPublic = stub.GetWalletPublicComponents(
            request=reqMasterPublic, metadata=metadata)
    except Exception as err:
        print('Error trying to get the public components of master :', err)

    print('Master Public key', resMasterPublic.public.hex())
    print('Master Chaincode :', resMasterPublic.chain_code.hex())
    print('Master Fingerprint :', resMasterPublic.parent_fingerprint.hex())
    print('Master Index key :', resMasterPublic.index)
    print('Depth of master key :', resMasterPublic.depth)
    try:
        reqPrivate = wallet_pb2.DeriveWalletPrivateKeyRequest(
            key_id=resMaster.key_id,
            whitelist=['ECDSA_SHA_512'],
            path=[2, 4, 1])
        resPrivate = stub.DeriveWalletPrivateKey(request=reqPrivate,
                                                 metadata=metadata)
        print('Private key id : ', resPrivate.key_id)
    except Exception as err:
        print('Error trying to derive private key : ', err)

    msgWallet = 'I want to sign this message using my wallet private key'
    try:
        reqSignWallet = sign_pb2.SignRequest(algorithm='ECDSA_SHA_512',
                                             key_id=resPrivate.key_id,
                                             data=msgWallet.encode())
        resSignWallet = stub.Sign(request=reqSignWallet, metadata=metadata)
        signatureWallet = resSignWallet.signature
        print('Signature using the wallet private key :', signature.hex())
    except Exception as err:
        print('There was an error signing the message:', err)
    try:
        reqPublicKey = wallet_pb2.DeriveWalletPublicKeyRequest(
            algorithm='BIP32',
            chain_code=resMasterPublic.chain_code,
            path=[2, 4, 1],
            public_key=resMasterPublic.public,
        )
        resPublicKey = stub.DeriveWalletPublicKey(request=reqPublicKey,
                                                  metadata=metadata)
    except Exception as err:
        print('Error deriving public key wallet:', err)
    print('Public key : ', resPublicKey.public_key.hex())
    print('Chaincode ', resPublicKey.chain_code.hex())
    try:
        req = sign_pb2.VerifySignatureRequest(
            algorithm='ECDSA_SHA_512',
            key=resPublicKey.public_key,
            signature=signatureWallet,
            data=msgWallet.encode(),
        )
        res = stub.VerifySignature(request=req, metadata=metadata)
        print('Signature is valid:', res.valid)
    except Exception as err:
        print('There was an error verifying the signature:', err)
    try:
        reqSeed = wallet_pb2.GenerateWalletSeedRequest(length=32)
        seedOneShot = stub.GenerateWalletSeed(request=reqSeed, metadata=metadata)
    except Exception as err:
        print('There was error getting entropy : ', err)
    try:
        reqPublicKeyOneShot = wallet_pb2.DeriveWalletPublicKeyOneShotRequest(
            algorithm='BIP32',
            wrapped=seedOneShot.wrapped_seed,
            path=[4, 8, 2])

        resPublicKeyOneShot = stub.DeriveWalletPublicKeyOneShot(request=reqPublicKeyOneShot,
                                               metadata=metadata)
        print('Public key is : ', resPublicKeyOneShot.public_key.hex())
        print('Chain code is : ', resPublicKeyOneShot.chain_code.hex())
    except Exception as err:
        print('Error deriving wallet public key OneShot : ', err)
    try:
        reqSignOneShot = wallet_pb2.DeriveWalletAndSignOneShotRequest(
            algorithm='BIP32',
            wrapped=seedOneShot.wrapped_seed,
            path=[4, 8, 2],
            signature_algorithm='ECDSA_SHA_512',
            data=msgWallet.encode())

        resSignOneShot = stub.DeriveWalletAndSignOneShot(request=reqSignOneShot,
                                               metadata=metadata)
        print('Signature is : ', resSignOneShot.signature.hex())
    except Exception as err:
        print('Error deriving wallet and sign OneShot : ', err)
        sys.exit(1)
    try:
        req = sign_pb2.VerifySignatureRequest(
            algorithm='ECDSA_SHA_512',
            key=resPublicKeyOneShot.public_key,
            signature=resSignOneShot.signature,
            data=msgWallet.encode(),
        )
        res = stub.VerifySignature(request=req, metadata=metadata)
        print('Signature is valid:', res.valid)
    except Exception as err:
        print('There was an error verifying the signature:', err)
      rspSeed <- client.generateWalletSeed(GenerateWalletSeedRequest(32))

      rspMasterWallet <- client.deriveWalletMasterKey(
        DeriveWalletMasterKeyRequest(
          "BIP32",
          Seq("ECDSA_SHA_512"),
          DeriveWalletMasterKeyRequest.Seed.Wrapped(rspSeed.wrappedSeed)
        )
      )
      _ = println("Master Key Id : " + rspMasterWallet.keyId)

      // It is possible to use a plaintext seed
      //
      // rspEntropy <- client.getEntropy(GetEntropyRequest(64, "PRNG"))
      //
      // ...
      // DeriveWalletMasterKeyRequest(
      //   "BIP32",
      //   Seq("ECDSA_SHA_512"),
      //   DeriveWalletMasterKeyRequest.Seed.Plaintext(rspEntropy.entropy)
      // )
      // ...

      rspWalletPublic <- client.getWalletPublicComponents(
        GetWalletPublicComponentsRequest(rspMasterWallet.keyId)
      )
      _ = println(
        "Master Public key : " + rspWalletPublic.public
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
      _ = println(
        "Master Chaincode : " + rspWalletPublic.chainCode
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
      _ = println(
        "Master Fingerprint : " + rspWalletPublic.parentFingerprint
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
      _ = println("Master Index key : " + rspWalletPublic.index)
      _ = println("Depth of master key : " + rspWalletPublic.depth)
      rspPrivateKey <- client.deriveWalletPrivateKey(
        DeriveWalletPrivateKeyRequest(
          Seq("ECDSA_SHA_512"),
          rspMasterWallet.keyId,
          Seq(2, 4, 1)
        )
      )
      _ = println("Private Key Id : " + rspPrivateKey.keyId)
      val msgSignWallet = "This message will be signed with wallet private key"
      rspSignWallet <- client.sign(
        SignRequest(
          ByteString.copyFromUtf8(msgSignWallet),
          rspPrivateKey.keyId,
          "ECDSA_SHA_512"
        )
      )
      _ = println(
        "Signature : " + rspSignWallet.signature
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
      rspPublicKey <- client.deriveWalletPublicKey(
        DeriveWalletPublicKeyRequest(
          "BIP32",
          rspWalletPublic.public,
          rspWalletPublic.chainCode,
          Seq(2, 4, 1)
        )
      )
      _ = println("Public key :" +
          rspPublicKey.publicKey
          .toByteArray()
          .map("%02x" format _)
          .mkString)
      _ = println("Chaincode : " +
          rspPublicKey.chainCode
          .toByteArray()
          .map("%02x" format _)
          .mkString)

      rspVerifySignWallet <- client.verifySignature(
        VerifySignatureRequest(
          ByteString.copyFromUtf8(msgSignWallet),
          rspSignWallet.signature,
          rspPublicKey.publicKey,
          "ECDSA_SHA_512"
        )
      )
      _ = println("Signature is valid : " + rspVerifySignWallet.valid)
      rspSeedOneShot <- client.generateWalletSeed(GenerateWalletSeedRequest(32))

      rspPublicKeyOneShot <- client.deriveWalletPublicKeyOneShot(
        DeriveWalletPublicKeyOneShotRequest(
          "BIP32",
          DeriveWalletPublicKeyOneShotRequest.Seed.Wrapped(rspSeedOneShot.wrappedSeed),
          Seq(4, 8, 2)
        )
      )
      _ = println("Public key is : " +
          rspPublicKeyOneShot.publicKey
          .toByteArray()
          .map("%02x" format _)
          .mkString)
      _ = println("Chain code is : " +
          rspPublicKeyOneShot.chainCode
          .toByteArray()
          .map("%02x" format _)
          .mkString)
      rspSignOneShot <- client.deriveWalletAndSignOneShot(
        DeriveWalletAndSignOneShotRequest(
          "BIP32",
          DeriveWalletAndSignOneShotRequest.Seed.Wrapped(rspSeedOneShot.wrappedSeed),
          Seq(4, 8, 2),
          ByteString.copyFromUtf8(msgSignWallet),
          "ECDSA_SHA_512"
        )
      )
      _ = println("Signature is : " +
          rspSignOneShot.signature
          .toByteArray()
          .map("%02x" format _)
          .mkString)

      rspVerifySignOneShot <- client.verifySignature(
        VerifySignatureRequest(
          ByteString.copyFromUtf8(msgSignWallet),
          rspSignOneShot.signature,
          rspPublicKeyOneShot.publicKey,
          "ECDSA_SHA_512"
        )
      )
      _ = println("Signature is valid : " + rspVerifySignOneShot.valid)
    var wrapped_seed = client.GenerateWalletSeed(
        new GenerateWalletSeedRequest {
          Length = 32,
        },
        metadata);

    var wallet_master = client.DeriveWalletMasterKey(
        new DeriveWalletMasterKeyRequest {
          Algorithm = "BIP32",
          Whitelist = { "ECDSA_SHA_512" },
          Wrapped = wrapped_seed.WrappedSeed,
        },
        metadata);

    // It is also possible to use a plaintext seed
    //
    // var wallet_seed = client.GetEntropy(
    //     new GetEntropyRequest { Source = "PRNG", Length = 64 }, metadata);
    //
    // ...
    // Plaintext = wallet_seed.Entropy
    // ...

    Console.WriteLine("Wallet Master Key: " + wallet_master.KeyId);
    // We can pull the public key of master in order to generate key pair for
    // m/2/4/1
    var master_public_components = client.GetWalletPublicComponents(
        new GetWalletPublicComponentsRequest { KeyId = wallet_master.KeyId },
        metadata);

    Console.WriteLine("Master Public key: " +
                      Bytes2Hex(master_public_components.Public));
    Console.WriteLine("Master Chaincode: " +
                      Bytes2Hex(master_public_components.ChainCode));
    Console.WriteLine("Master Fingerprint: " +
                      Bytes2Hex(master_public_components.ParentFingerprint));
    Console.WriteLine("Master Index key: " + master_public_components.Index);
    Console.WriteLine("Depth of master key: " + master_public_components.Depth);
    // ANCHOR_END get_public_wallet

    var wallet_public_first = client.DeriveWalletPublicKey(
        new DeriveWalletPublicKeyRequest {
          Algorithm = "BIP32", PublicKey = master_public_components.Public,
          ChainCode = master_public_components.ChainCode, Path = { 2, 4, 1 }
        },
        metadata);

    // Reusing the same path to generate public key of the freshly generated
    // private key
    Console.WriteLine("Public key of m/2/4/1: " +
                      Bytes2Hex(wallet_public_first.PublicKey));
    Console.WriteLine("Chaincode of public key of m/2/4/1: " +
                      Bytes2Hex(wallet_public_first.ChainCode));

    var testSignature = client.VerifySignature(
        new VerifySignatureRequest {
          Algorithm = "ECDSA_SHA_512", Key = wallet_public_first.PublicKey,
          Signature = signature_wallet.Signature,
          Data = ByteString.CopyFromUtf8(wallet_message)
        },
        metadata);
    Console.WriteLine("Signature is valid: " + testSignature);

    var wrapped_oneshot_seed = client.GenerateWalletSeed(
        new GenerateWalletSeedRequest {
          Length = 32,
        },
        metadata);

    var wallet_public_oneshot = client.DeriveWalletPublicKeyOneShot(
        new DeriveWalletPublicKeyOneShotRequest {
          Algorithm = "BIP32", Wrapped = wrapped_oneshot_seed.WrappedSeed,
          Path = { 4, 8, 2 }
        },
        metadata);

    Console.WriteLine("Public key is : " +
                      Bytes2Hex(wallet_public_oneshot.PublicKey));
    Console.WriteLine("Chain code is : " +
                      Bytes2Hex(wallet_public_oneshot.ChainCode));

    var wallet_sign_oneshot = client.DeriveWalletAndSignOneShot(
        new DeriveWalletAndSignOneShotRequest {
          Algorithm = "BIP32", Wrapped = wrapped_oneshot_seed.WrappedSeed,
          Path = { 4, 8, 2 }, SignatureAlgorithm = "ECDSA_SHA_512",
          Data = ByteString.CopyFromUtf8(wallet_message)
        },
        metadata);

    Console.WriteLine("Signature is : " +
                      Bytes2Hex(wallet_sign_oneshot.Signature));

    var testOneShotSignature = client.VerifySignature(
        new VerifySignatureRequest {
          Algorithm = "ECDSA_SHA_512", Key = wallet_public_oneshot.PublicKey,
          Signature = wallet_sign_oneshot.Signature,
          Data = ByteString.CopyFromUtf8(wallet_message)
        },
        metadata);
    Console.WriteLine("Signature is valid: " + testOneShotSignature);

    var msgAuth = "I want to authenticate this message";
    var replyAuthenticateMessage = client.AuthenticateMessage(
        new AuthenticateMessageRequest { Algorithm = "HMAC_SHA_512", 
                                         KeyId = keyIdAes,
                                         Plaintext = ByteString.CopyFromUtf8(msgAuth)},
        metadata);

    Console.WriteLine("Authenticate tag: " + Bytes2Hex(replyAuthenticateMessage.Tag));

    var replyVerifyTag = client.VerifyTag(
        new VerifyTagRequest { Algorithm = "HMAC_SHA_512", 
                               KeyId = keyIdAes,
                               Plaintext = ByteString.CopyFromUtf8(msgAuth),
                               Tag = replyAuthenticateMessage.Tag},
        metadata);

    Console.WriteLine("Authenticate tag is valid: " + replyVerifyTag.Success);

    var replyAuthenticateMessageWithWrapped = client.AuthenticateMessageWithWrapped(
        new AuthenticateMessageWithWrappedRequest { Algorithm = "HMAC_SHA_512", 
                                                    WrappedKey = respGenWrappedAes.WrappedSecret,
                                                    Plaintext = ByteString.CopyFromUtf8(msgAuth)},
        metadata);

    Console.WriteLine("Authenticate tag: " + Bytes2Hex(replyAuthenticateMessageWithWrapped.Tag));

    var replyVerifyTagWithWrapped = client.VerifyTagWithWrapped(
        new VerifyTagWithWrappedRequest { Algorithm = "HMAC_SHA_512", 
                                          WrappedKey = respGenWrappedAes.WrappedSecret,
                                          Plaintext = ByteString.CopyFromUtf8(msgAuth),
                                          Tag = replyAuthenticateMessageWithWrapped.Tag},
        metadata);

    Console.WriteLine("Authenticate tag is valid: " + replyVerifyTagWithWrapped.Success);
  }
}
}
    var wallet_private_first = client.DeriveWalletPrivateKey(
        new DeriveWalletPrivateKeyRequest { Whitelist = { "ECDSA_SHA_512" },
                                            KeyId = wallet_master.KeyId,
                                            Path = { 2, 4, 1 } },
        metadata);

    Console.WriteLine("wallet m/2/4/1 Private Key: " +
                      wallet_private_first.KeyId);
    // We can use this key ID with the given signatures algorithm
    var wallet_message = "I want to sign this message !";

    var signature_wallet = client.Sign(
        new SignRequest { Data = ByteString.CopyFromUtf8(wallet_message),
                          KeyId = wallet_private_first.KeyId,
                          Algorithm = "ECDSA_SHA_512" },
        metadata);

    Console.WriteLine("Signed message with derived private key: " +
                      signature_wallet.Signature.ToStringUtf8());
    var wallet_public_first = client.DeriveWalletPublicKey(
        new DeriveWalletPublicKeyRequest {
          Algorithm = "BIP32", PublicKey = master_public_components.Public,
          ChainCode = master_public_components.ChainCode, Path = { 2, 4, 1 }
        },
        metadata);

    // Reusing the same path to generate public key of the freshly generated
    // private key
    Console.WriteLine("Public key of m/2/4/1: " +
                      Bytes2Hex(wallet_public_first.PublicKey));
    Console.WriteLine("Chaincode of public key of m/2/4/1: " +
                      Bytes2Hex(wallet_public_first.ChainCode));

    var testSignature = client.VerifySignature(
        new VerifySignatureRequest {
          Algorithm = "ECDSA_SHA_512", Key = wallet_public_first.PublicKey,
          Signature = signature_wallet.Signature,
          Data = ByteString.CopyFromUtf8(wallet_message)
        },
        metadata);
    Console.WriteLine("Signature is valid: " + testSignature);
    var wrapped_oneshot_seed = client.GenerateWalletSeed(
        new GenerateWalletSeedRequest {
          Length = 32,
        },
        metadata);

    var wallet_public_oneshot = client.DeriveWalletPublicKeyOneShot(
        new DeriveWalletPublicKeyOneShotRequest {
          Algorithm = "BIP32", Wrapped = wrapped_oneshot_seed.WrappedSeed,
          Path = { 4, 8, 2 }
        },
        metadata);

    Console.WriteLine("Public key is : " +
                      Bytes2Hex(wallet_public_oneshot.PublicKey));
    Console.WriteLine("Chain code is : " +
                      Bytes2Hex(wallet_public_oneshot.ChainCode));
    var wallet_sign_oneshot = client.DeriveWalletAndSignOneShot(
        new DeriveWalletAndSignOneShotRequest {
          Algorithm = "BIP32", Wrapped = wrapped_oneshot_seed.WrappedSeed,
          Path = { 4, 8, 2 }, SignatureAlgorithm = "ECDSA_SHA_512",
          Data = ByteString.CopyFromUtf8(wallet_message)
        },
        metadata);

    Console.WriteLine("Signature is : " +
                      Bytes2Hex(wallet_sign_oneshot.Signature));

    var testOneShotSignature = client.VerifySignature(
        new VerifySignatureRequest {
          Algorithm = "ECDSA_SHA_512", Key = wallet_public_oneshot.PublicKey,
          Signature = wallet_sign_oneshot.Signature,
          Data = ByteString.CopyFromUtf8(wallet_message)
        },
        metadata);
    Console.WriteLine("Signature is valid: " + testOneShotSignature);
  std::vector<std::string> whitelist_wallet;
  whitelist_wallet.push_back("ECDSA_SHA_512");
  // Usually use BIP-0039 to seed, here seede randomly for the example
  std::string wallet_master =
      subtle_crypto.DeriveWalletMasterKey("BIP32", whitelist_wallet);

  std::cout << "Wallet Master Key : " << wallet_master << std::endl;
  // We can pull the public key of master in order to generate key pair for
  // m/2/4/1
  crypto::GetWalletPublicComponentsResponse master_public_components =
      subtle_crypto.GetWalletPublicComponents(wallet_master);

  print_bytes("Master Public key : ", master_public_components.public_());
  print_bytes("Master Chaincode : ", master_public_components.chain_code());
  print_bytes("Master Fingerprint : ",
              master_public_components.parent_fingerprint());
  std::cout << "Master Index key : " << master_public_components.index()
            << std::endl;
  std::cout << "Depth of master key : " << master_public_components.depth()
            << std::endl;
  // ANCHOR_END get_public_wallet

  std::vector<std::uint32_t> path_first_public;
  // In this example we don't use hardened keys to derive private and public and
  // show how the API works The path will be m/2/4/1, if you want an hardened
  // key you need to have the MSB to 1 (so 0H is 2147483648)
  path_first_public.push_back(2);
  path_first_public.push_back(4);
  path_first_public.push_back(1);
  // Reusing the same path to generate public key of the freshly generated
  // private key
  crypto::DeriveWalletPublicKeyResponse wallet_public_first =
      subtle_crypto.DeriveWalletPublicKey(
          "BIP32", master_public_components.public_(),
          master_public_components.chain_code(), path_first_public);
  print_bytes("Public key of m/2/4/1 : ", wallet_public_first.public_key());
  print_bytes("Chaincode of public key of m/2/4/1 : ",
              wallet_public_first.chain_code());
  bool test_signature = subtle_crypto.VerifySignature(
      "ECDSA_SHA_512", wallet_public_first.public_key(), signature_wallet,
      wallet_message);
  std::cout << "Signature is valid: " << test_signature << std::endl;

  std::string wallet_wrapped_seed = subtle_crypto.GenerateWalletSeed();

  std::vector<std::uint32_t> path_oneshot;
  // In this example we don't use hardened keys to derive private and public and
  // show how the API works The path will be m/4/8/2, if you want an hardened
  // key you need to have the MSB to 1 (so 0H is 2147483648)
  path_oneshot.push_back(4);
  path_oneshot.push_back(8);
  path_oneshot.push_back(2);

  crypto::DeriveWalletPublicKeyOneShotResponse wallet_public_oneshot =
      subtle_crypto.DeriveWalletPublicKeyOneShot(
          "BIP32", wallet_wrapped_seed, path_oneshot);
  print_bytes("Public key of m/4/8/2 : ", wallet_public_oneshot.public_key());
  print_bytes("Chaincode of public key of m/4/8/2 : ",
              wallet_public_oneshot.chain_code());

  std::string wallet_oneshot_message = "I want to sign this message !";

  crypto::DeriveWalletAndSignOneShotResponse wallet_sign_oneshot =
      subtle_crypto.DeriveWalletAndSignOneShot(
          "BIP32", wallet_wrapped_seed, path_oneshot, "ECDSA_SHA_512", wallet_oneshot_message);

  print_bytes("Signature of key m/4/8/2 : ", wallet_sign_oneshot.signature());

  bool test_one_shot_signature = subtle_crypto.VerifySignature(
      "ECDSA_SHA_512", wallet_public_oneshot.public_key(), wallet_sign_oneshot.signature(),
      wallet_oneshot_message);
  std::cout << "Signature is valid: " << test_one_shot_signature << std::endl;

  // Authenticate a message
  std::string tag_msg("I want to authenticate this message");
  std::string tag =
      subtle_crypto.AuthenticateMessage("HMAC_SHA_512", key_id_aes, tag_msg);
  print_bytes("Authentication tag: ", tag);

  // Verify a tag
  bool verify_tag_success =
      subtle_crypto.VerifyTag("HMAC_SHA_512", key_id_aes, tag_msg, tag);

  std::cout << "Autentication tag is valid: " 
            << std::boolalpha 
            << verify_tag_success 
            << std::endl;

  // Authenticate a message with wrapped key
  std::string tag_with_wrapped =
      subtle_crypto.AuthenticateMessageWithWrapped("HMAC_SHA_512",
                                                   wrapped_key_aes.wrapped_secret(),
                                                   tag_msg);
  print_bytes("Authentication tag: ", tag);

  // Verify a tag with wrapped key
  bool verify_tag_with_wrapped_success =
      subtle_crypto.VerifyTagWithWrapped("HMAC_SHA_512",
                                         wrapped_key_aes.wrapped_secret(),
                                         tag_msg,
                                         tag_with_wrapped);

  std::cout << "Autentication tag is valid: " 
            << std::boolalpha 
            << verify_tag_with_wrapped_success 
            << std::endl;

  return 0;
}
  std::vector<std::uint32_t> path_first_key;
  // In this example we don't use hardened keys to derive private and public
  // keys and show how the API works The path will be m/2/4/1, if you want an
  // hardened key you need to have the MSB to 1 (so 0H is 2147483648)
  path_first_key.push_back(2);
  path_first_key.push_back(4);
  path_first_key.push_back(1);

  std::string wallet_private_first = subtle_crypto.DeriveWalletPrivateKey(
      wallet_master, whitelist_wallet, path_first_key);

  std::cout << "wallet m/2/4/1 Private Key : " << wallet_private_first
            << std::endl;
  // We can use this key ID with the given signatures algorithm
  std::string wallet_message = "I want to sign this message !";
  std::string signature_wallet =
      subtle_crypto.Sign("ECDSA_SHA_512", wallet_private_first, wallet_message);
  print_bytes("Signed message with derived private key : ", signature_wallet);
  std::vector<std::uint32_t> path_first_public;
  // In this example we don't use hardened keys to derive private and public and
  // show how the API works The path will be m/2/4/1, if you want an hardened
  // key you need to have the MSB to 1 (so 0H is 2147483648)
  path_first_public.push_back(2);
  path_first_public.push_back(4);
  path_first_public.push_back(1);
  // Reusing the same path to generate public key of the freshly generated
  // private key
  crypto::DeriveWalletPublicKeyResponse wallet_public_first =
      subtle_crypto.DeriveWalletPublicKey(
          "BIP32", master_public_components.public_(),
          master_public_components.chain_code(), path_first_public);
  print_bytes("Public key of m/2/4/1 : ", wallet_public_first.public_key());
  print_bytes("Chaincode of public key of m/2/4/1 : ",
              wallet_public_first.chain_code());
  bool test_signature = subtle_crypto.VerifySignature(
      "ECDSA_SHA_512", wallet_public_first.public_key(), signature_wallet,
      wallet_message);
  std::cout << "Signature is valid: " << test_signature << std::endl;
  std::string wallet_wrapped_seed = subtle_crypto.GenerateWalletSeed();

  std::vector<std::uint32_t> path_oneshot;
  // In this example we don't use hardened keys to derive private and public and
  // show how the API works The path will be m/4/8/2, if you want an hardened
  // key you need to have the MSB to 1 (so 0H is 2147483648)
  path_oneshot.push_back(4);
  path_oneshot.push_back(8);
  path_oneshot.push_back(2);

  crypto::DeriveWalletPublicKeyOneShotResponse wallet_public_oneshot =
      subtle_crypto.DeriveWalletPublicKeyOneShot(
          "BIP32", wallet_wrapped_seed, path_oneshot);
  print_bytes("Public key of m/4/8/2 : ", wallet_public_oneshot.public_key());
  print_bytes("Chaincode of public key of m/4/8/2 : ",
              wallet_public_oneshot.chain_code());
  std::string wallet_oneshot_message = "I want to sign this message !";

  crypto::DeriveWalletAndSignOneShotResponse wallet_sign_oneshot =
      subtle_crypto.DeriveWalletAndSignOneShot(
          "BIP32", wallet_wrapped_seed, path_oneshot, "ECDSA_SHA_512", wallet_oneshot_message);

  print_bytes("Signature of key m/4/8/2 : ", wallet_sign_oneshot.signature());

  bool test_one_shot_signature = subtle_crypto.VerifySignature(
      "ECDSA_SHA_512", wallet_public_oneshot.public_key(), wallet_sign_oneshot.signature(),
      wallet_oneshot_message);
  std::cout << "Signature is valid: " << test_one_shot_signature << std::endl;

    }
  )
    client.getWalletPublicComponents(
      {
        key_id: resWalletMaster.key_id
      },
      metadata,
      (err, resPublicWallet) => {
        console.log(err || `Master Public key : ${resPublicWallet.public.toString('hex')}`)
        console.log(err || `Master Chaincode : ${resPublicWallet.chain_code.toString('hex')}`)
        console.log(err || `Master Fingerprint : ${resPublicWallet.parent_fingerprint}`)
        console.log(err || `Master Index key : ${resPublicWallet.index}`)
        console.log(err || `Depth of master key : ${resPublicWallet.depth}`)
    }
  )
    client.deriveWalletPrivateKey(
      {
        key_id: resWalletMaster.key_id,
        whitelist: ['ECDSA_SHA_512'],
        path: [2, 4, 1]
      },
      metadata,
      (err, resPrivate) => {
        console.log(err || `Private KeyId: ${resPrivate.key_id}`)
    }
  )

    // Sign a message using the previously generated key
    client.Sign(
      {
        algorithm: 'ECDSA_SHA_512',
        data: Buffer.from('Message to sign', 'utf8'),
        key_id: resPrivate.key_id
      },
      metadata,
      (err, resSignWallet) => {
        console.log(err || `Signature: ${resSignWallet.signature.toString('hex')}`)
    }
  )
    client.deriveWalletPublicKey(
      {
        algorithm: 'BIP32',
        chain_code: resPublicWallet.chain_code,
        path: [2, 4, 1],
        public_key: resPublicWallet.public
      },
      metadata,
      (err, resPublicKey) => {
        console.log(err || `Public KeyId: ${resPublicKey.public_key.toString('hex')}`)
        console.log(err || `Chaincode: ${resPublicKey.chain_code.toString('hex')}`)
    }
  )

    // Verify the signature of the message
    client.VerifySignature(
      {
        key: resPublicKey.public_key,
        algorithm: 'ECDSA_SHA_512',
        signature: resSignWallet.signature,
        data: Buffer.from('Message to sign', 'utf8')
      },
      metadata,
      (err, resVerify) => {
        console.log(err || `Signature is valid: ${resVerify.valid}`)
    }
  )
    client.deriveWalletPublicKeyOneShot(
      {
        algorithm: 'BIP32',
        wrapped: resSeed.wrapped_seed,
        path: [4, 8, 2]
      },
      metadata,
      (err, resPublicKey) => {
        console.log(err || `Public KeyId: ${resPublicKey.public_key.toString('hex')}`)
        console.log(err || `Chaincode: ${resPublicKey.chain_code.toString('hex')}`)
    }
  )
    client.deriveWalletAndSignOneShot(
      {
        algorithm: 'BIP32',
        wrapped: resSeed.wrapped_seed,
        path: [4, 8, 2],
        signature_algorithm: 'ECDSA_SHA_512',
        data: Buffer.from('Message to sign', 'utf8')
      },
      metadata,
      (err, resSign) => {
        console.log(err || `Signature: ${resSign.signature.toString('hex')}`)
    }
  )

    // Verify the signature of the message
    client.VerifySignature(
      {
        key: resPublicKey.public_key,
        algorithm: 'ECDSA_SHA_512',
        signature: resSign.signature,
        data: Buffer.from('Message to sign', 'utf8')
      },
      metadata,
      (err, resVerify) => {
        console.log(err || `Signature is valid: ${resVerify.valid}`)
    }
  )
    GenerateWalletSeedRequest reqSeed =
        GenerateWalletSeedRequest.newBuilder().setLength(32).build();

    GenerateWalletSeedResponse respSeed = client.generateWalletSeed(reqSeed);

    DeriveWalletMasterKeyRequest reqMaster =
        DeriveWalletMasterKeyRequest.newBuilder()
            .setAlgorithm("BIP32")
            .addWhitelist("ECDSA_SHA_512")
            .setWrapped(respSeed.getWrappedSeed())
            .build();

    /*
    It is possible to use a plaintext seed

    GetEntropyRequest reqEntropyWallet =
    GetEntropyRequest.newBuilder().setSource("PRNG").setLength(64).build();

    GetEntropyResponse respEntropyWallet = client.getEntropy(reqEntropyWallet);

    ...
    .setPlaintext(respEntropyWallet.getEntropy())
    ...
    */

    DeriveWalletMasterKeyResponse respMaster =
        client.deriveWalletMasterKey(reqMaster);
    System.out.println("Master key id : " + respMaster.getKeyId());
    GetWalletPublicComponentsRequest reqPubWallet =
        GetWalletPublicComponentsRequest.newBuilder()
            .setKeyId(respMaster.getKeyId())
            .build();

    GetWalletPublicComponentsResponse respPubWallet =
        client.getWalletPublicComponents(reqPubWallet);
    System.out.println("Master Public key : " + respPubWallet.getPublic());
    System.out.println("Master Chaincode : " + respPubWallet.getChainCode());
    System.out.println("Master Fingerprint : " +
                       respPubWallet.getParentFingerprint());
    System.out.println("Master Index key : " + respPubWallet.getIndex());
    System.out.println("Depth of master key : " + respPubWallet.getDepth());
    DeriveWalletPrivateKeyRequest reqPrivate =
        DeriveWalletPrivateKeyRequest.newBuilder()
            .setKeyId(respMaster.getKeyId())
            .addWhitelist("ECDSA_SHA_512")
            .addPath(2)
            .addPath(4)
            .addPath(1)
            .build();

    DeriveWalletPrivateKeyResponse respPrivate =
        client.deriveWalletPrivateKey(reqPrivate);
    System.out.println("Private key id : " + respPrivate.getKeyId());

    SignRequest reqSignWallet =
        SignRequest.newBuilder()
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .setAlgorithm("ECDSA_SHA_512")
            .setKeyId(respPrivate.getKeyId())
            .build();

    SignResponse respSignWallet = client.sign(reqSignWallet);

    System.out.println("Signature: " + respSignWallet.getSignature());
    DeriveWalletPublicKeyRequest reqPublicKeyWallet =
        DeriveWalletPublicKeyRequest.newBuilder()
            .setAlgorithm("BIP32")
            .setPublicKey(respPubWallet.getPublic())
            .setChainCode(respPubWallet.getChainCode())
            .addPath(2)
            .addPath(4)
            .addPath(1)
            .build();

    DeriveWalletPublicKeyResponse respPublicKeyWallet =
        client.deriveWalletPublicKey(reqPublicKeyWallet);
    System.out.println("Public key : " + respPublicKeyWallet.getPublicKey());
    System.out.println("Chaincode key : " + respPublicKeyWallet.getChainCode());

    VerifySignatureRequest reqVerifyWallet =
        VerifySignatureRequest.newBuilder()
            .setKey(respPublicKeyWallet.getPublicKey())
            .setAlgorithm("ECDSA_SHA_512")
            .setSignature(respSignWallet.getSignature())
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .build();

    VerifySignatureResponse respVerifyWallet =
        client.verifySignature(reqVerifyWallet);

    System.out.println("Signature is valid: " + respVerifyWallet.getValid());
    GenerateWalletSeedRequest reqSeedOneShot =
        GenerateWalletSeedRequest.newBuilder().setLength(32).build();

    GenerateWalletSeedResponse respSeedOneShot = client.generateWalletSeed(reqSeedOneShot);

    DeriveWalletPublicKeyOneShotRequest reqPublicKeyOneShot =
    DeriveWalletPublicKeyOneShotRequest.newBuilder()
            .setAlgorithm("BIP32")
            .setWrapped(respSeedOneShot.getWrappedSeed())
            .addPath(4)
            .addPath(8)
            .addPath(2)
            .build();

    DeriveWalletPublicKeyOneShotResponse respPublicKeyOneShot =
        client.deriveWalletPublicKeyOneShot(reqPublicKeyOneShot);
    System.out.println("Public key is : " + respPublicKeyOneShot.getPublicKey());
    System.out.println("Chain code is : " + respPublicKeyOneShot.getChainCode());
    DeriveWalletAndSignOneShotRequest reqSignOneShot =
    DeriveWalletAndSignOneShotRequest.newBuilder()
            .setAlgorithm("BIP32")
            .setWrapped(respSeedOneShot.getWrappedSeed())
            .addPath(4)
            .addPath(8)
            .addPath(2)
            .setSignatureAlgorithm("ECDSA_SHA_512")
            .setData(ByteString.copyFromUtf8("Message to sign"))
            .build();

    DeriveWalletAndSignOneShotResponse respSignOneShot =
        client.deriveWalletAndSignOneShot(reqSignOneShot);
    System.out.println("Signature is : " + respSignOneShot.getSignature());

    VerifySignatureRequest reqVerifyOneShot =
    VerifySignatureRequest.newBuilder()
        .setKey(respPublicKeyOneShot.getPublicKey())
        .setAlgorithm("ECDSA_SHA_512")
        .setSignature(respSignOneShot.getSignature())
        .setData(ByteString.copyFromUtf8("Message to sign"))
        .build();

    VerifySignatureResponse respVerifyOneShot =
        client.verifySignature(reqVerifyOneShot);

    System.out.println("Signature is valid: " + respVerifyOneShot.getValid());

The cryptographic API supports blockchain wallet key derivation using algorithms such as SLIP-0010 and BIP-0032. A set of RPCs is provided to allow:

The attached code examples use all of these functionalities.

The last two calls are a bit special in the way they work. They are suffixed with "OneShot" because they do not keep any intermediate steps. This means that the operations perform a master key generation, then a private key derivation on each call. These steps are not returned to the caller and are not stored by the crypto service. For signature verification, you need to retrieve the public key with the DeriveWalletPublicKeyOneShot command.

Authenticating messages

    // Authenticate a message
    let response = client
    .authenticate_message(AuthenticateMessageRequest {
        plaintext: b"I want to authenticate this message".to_vec(),
        key_id: key_id_aes.clone(),
        algorithm: "HMAC_SHA_512".to_string(),
    })
    .await?;

    let tag = response.into_inner().tag;
    println!("Authentication tag: {:?}", tag);
    // Authenticate a message with wrapped key
    let response = client
    .authenticate_message_with_wrapped(AuthenticateMessageWithWrappedRequest {
        plaintext: b"I want to authenticate this message".to_vec(),
        wrapped_key: wrapped_key_aes.clone(),
        algorithm: "HMAC_SHA_512".to_string(),
    })
    .await?;

    let tag = response.into_inner().tag;
    println!("Authentication tag: {:?}", tag);
	// Authenticate a message
	msg := "I want to authenticate this message"
	rsp, err := client.AuthenticateMessage(ctx, &crypto.AuthenticateMessageRequest{
		Plaintext: []byte(msg),
		KeyId:     keyId,
		Algorithm: "HMAC_SHA_512",
	})

	// Check err for errors

	tag := rsp.Tag
	fmt.Printf("Authentication tag: %x\n", tag)
	// Authenticate a message with wrapped key
	msg := "I want to authenticate this message"
	rsp, err := client.AuthenticateMessageWithWrapped(ctx, &crypto.AuthenticateMessageWithWrappedRequest{
		Plaintext:  []byte(msg),
		WrappedKey: wrappedKey.WrappedSecret,
		Algorithm:  "HMAC_SHA_512",
	})

	// Check err for errors

	tag := rsp.Tag
	fmt.Printf("Authentication tag: %x\n", tag)
    # Authenticate a message
    msg = 'I want to authenticate this message'
    try:
        req = mac_pb2.AuthenticateMessageRequest(plaintext=msg.encode(),
                                                 key_id=key_id_aes,
                                                 algorithm='HMAC_SHA_512')
        res = stub.AuthenticateMessage(request=req, metadata=metadata)
        tag = res.tag
        print('Authentication tag:', tag)
    except Exception as err:
        print('There was an error authenticating the message:', err)
    # Authenticate a message with wrapped key
    msg = 'I want to authenticate this message'
    try:
        req = mac_pb2.AuthenticateMessageWithWrappedRequest(plaintext=msg.encode(),
                                                            wrapped_key=wrapped_key_aes,
                                                            algorithm='HMAC_SHA_512')
        res = stub.AuthenticateMessageWithWrapped(request=req, metadata=metadata)
        tag = res.tag
        print('Authentication tag:', tag)
    except Exception as err:
        print('There was an error authenticating the message:', err)
      // Authenticate a message
      val msg: String = "I want to authenticate this message"
      rspAuthenticateMessage <- client.authenticateMessage(
        AuthenticateMessageRequest(
          ByteString.copyFromUtf8(msg),
          rspGenAes.keyId,
          "HMAC_SHA_512"
        )
      )
      _ = println(
        "Authentication tag: " + rspAuthenticateMessage.tag
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
      // Authenticate a message
      val msg: String = "I want to authenticate this message"
      rspAuthenticateMessage <- client.authenticateMessage(
        AuthenticateMessageRequest(
          ByteString.copyFromUtf8(msg),
          rspGenAes.keyId,
          "HMAC_SHA_512"
        )
      )
      _ = println(
        "Authentication tag: " + rspAuthenticateMessage.tag
          .toByteArray()
          .map("%02x" format _)
          .mkString
      )
    var msgAuth = "I want to authenticate this message";
    var replyAuthenticateMessage = client.AuthenticateMessage(
        new AuthenticateMessageRequest { Algorithm = "HMAC_SHA_512", 
                                         KeyId = keyIdAes,
                                         Plaintext = ByteString.CopyFromUtf8(msgAuth)},
        metadata);

    Console.WriteLine("Authenticate tag: " + Bytes2Hex(replyAuthenticateMessage.Tag));
    var replyAuthenticateMessageWithWrapped = client.AuthenticateMessageWithWrapped(
        new AuthenticateMessageWithWrappedRequest { Algorithm = "HMAC_SHA_512", 
                                                    WrappedKey = respGenWrappedAes.WrappedSecret,
                                                    Plaintext = ByteString.CopyFromUtf8(msgAuth)},
        metadata);

    Console.WriteLine("Authenticate tag: " + Bytes2Hex(replyAuthenticateMessageWithWrapped.Tag));
  // Inside the public part of class SubtleCryptoClient
  std::string AuthenticateMessage(const std::string &algorithm, 
                                  const std::string &key_id,
                                  const std::string &plaintext) {
    crypto::AuthenticateMessageRequest request;
    request.set_algorithm(algorithm);
    request.set_key_id(key_id);
    request.set_plaintext(plaintext);

    crypto::AuthenticateMessageResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->AuthenticateMessage(&context, request, &response);

    if (status.ok()) {
      return response.tag();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Authenticate a message
  std::string tag_msg("I want to authenticate this message");
  std::string tag =
      subtle_crypto.AuthenticateMessage("HMAC_SHA_512", key_id_aes, tag_msg);
  print_bytes("Authentication tag: ", tag);
  // Inside the public part of class SubtleCryptoClient
  std::string AuthenticateMessageWithWrapped(const std::string &algorithm, 
                                  const std::string &wrapped_key,
                                  const std::string &plaintext) {
    crypto::AuthenticateMessageWithWrappedRequest request;
    request.set_algorithm(algorithm);
    request.set_wrapped_key(wrapped_key);
    request.set_plaintext(plaintext);

    crypto::AuthenticateMessageResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->AuthenticateMessageWithWrapped(&context, request, &response);

    if (status.ok()) {
      return response.tag();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Authenticate a message with wrapped key
  std::string tag_with_wrapped =
      subtle_crypto.AuthenticateMessageWithWrapped("HMAC_SHA_512",
                                                   wrapped_key_aes.wrapped_secret(),
                                                   tag_msg);
  print_bytes("Authentication tag: ", tag);
    // Authenticate a message
    client.AuthenticateMessage(
      {
        algorithm: 'HMAC_SHA_512',
        plaintext: Buffer.from('I want to authenticate this message', 'utf8'),
        key_id: keyIdAes
      },
      metadata,
      (err, resAuthenticateMessage) => {
        console.log(err || `Tag: ${resAuthenticateMessage.tag.toString('hex')}`)
    }
  )
    // Authenticate a message with wrapped key
    client.AuthenticateMessageWithWrapped(
      {
        algorithm: 'HMAC_SHA_512',
        plaintext: Buffer.from('I want to authenticate this message', 'utf8'),
        wrapped_key: wrappedKeyAes
      },
      metadata,
      (err, resAuthenticateMessageWithWrapped) => {
        console.log(err || `Authentication tag: ${resAuthenticateMessageWithWrapped.tag.toString('hex')}`)
    }
  )
    // Authenticate a message
    AuthenticateMessageRequest reqAuthenticateMessage =
        AuthenticateMessageRequest.newBuilder()
            .setKeyId(respGenAes.getKeyId())
            .setAlgorithm("HMAC_SHA_512")
            .setPlaintext(ByteString.copyFromUtf8("I want to authenticate this message"))
            .build();
    
    AuthenticateMessageResponse respAuthenticateMessage =
        client.authenticateMessage(reqAuthenticateMessage);

    System.out.println("Authentication tag: " + respAuthenticateMessage.getTag());
    // Authenticate a message with wrapped key
    AuthenticateMessageWithWrappedRequest reqAuthenticateMessageWithWrapped =
        AuthenticateMessageWithWrappedRequest.newBuilder()
            .setWrappedKey(respGenWrapAes.getWrappedSecret())
            .setAlgorithm("HMAC_SHA_512")
            .setPlaintext(ByteString.copyFromUtf8("I want to authenticate this message"))
            .build();
    
    AuthenticateMessageResponse respAuthenticateMessageWithWrapped =
        client.authenticateMessageWithWrapped(reqAuthenticateMessageWithWrapped);

    System.out.println("Authentication tag: " + respAuthenticateMessage.getTag());

After the generation of a wrapped key or a key with authentication capabilities, one can perform authentication tags with a given algorithm on provided data.

If the key represented by the key_id does not exist, meaning its id does not exist or the key has not been generated by the currently pinned backend, or the key does not whitelist the selected algorithm, an error is returned. If all conditions are fulfilled, an authentication tag is returned.

In the response, the tag field contains the raw bytes of the message authentication code.

Verifying tags

    // Verify a tag
    let response = client
    .verify_tag(VerifyTagRequest {
        plaintext: b"I want to authenticate this message".to_vec(),
        key_id: key_id_aes.clone(),
        algorithm: "HMAC_SHA_512".to_string(),
        tag,
    })
    .await?;

    let success = response.into_inner().success;
    println!("Authentication tag is valid: {:?}", success);
    // Verify a tag with wrapped key
    let response = client
    .verify_tag_with_wrapped(VerifyTagWithWrappedRequest {
        plaintext: b"I want to authenticate this message".to_vec(),
        wrapped_key: wrapped_key_aes.clone(),
        algorithm: "HMAC_SHA_512".to_string(),
        tag,
    })
    .await?;

    let success = response.into_inner().success;
    println!("Authentication tag is valid: {:?}", success);
	// Verify a tag
	msg := "I want to authenticate this message"
	rsp, err := client.VerifyTag(ctx, &crypto.VerifyTagRequest{
		Plaintext: []byte(msg),
		KeyId:     keyId,
		Algorithm: "HMAC_SHA_512",
		Tag:       tag,
	})

	// Check err for errors

	success := rsp.Success
	fmt.Printf("Authentication tag is valid: %x\n", success)
	// Verify a tag with wrapped key
	msg := "I want to authenticate this message"
	rsp, err := client.VerifyTagWithWrapped(ctx, &crypto.VerifyTagWithWrappedRequest{
		Plaintext:  []byte(msg),
		WrappedKey: wrappedKey.WrappedSecret,
		Algorithm:  "HMAC_SHA_512",
		Tag:        tag,
	})

	// Check err for errors

	success := rsp.Success
	fmt.Printf("Authentication tag is valid: %x\n", success)
    # Verify a tag
    msg = 'I want to authenticate this message'
    try:
        req = mac_pb2.VerifyTagRequest(plaintext=msg.encode(),
                                       key_id=key_id_aes,
                                       algorithm='HMAC_SHA_512',
                                       tag=tag)
        res = stub.VerifyTag(request=req, metadata=metadata)
        success = res.success
        print('Authentication tag is valid:', success)
    except Exception as err:
        print('There was an error verifying the message tag:', err)
    # Verify a tag with wrapped key
    msg = 'I want to authenticate this message'
    try:
        req = mac_pb2.VerifyTagWithWrappedRequest(plaintext=msg.encode(),
                                                  wrapped_key=wrapped_key_aes,
                                                  algorithm='HMAC_SHA_512',
                                                  tag=tag)
        res = stub.VerifyTagWithWrapped(request=req, metadata=metadata)
        success = res.success
        print('Authentication tag is valid:', success)
    except Exception as err:
        print('There was an error verifying the message tag:', err)
      // Verify a tag
      rspVerifyTag <- client.verifyTag(
        VerifyTagRequest(
          ByteString.copyFromUtf8(msg),
          rspGenAes.keyId,
          "HMAC_SHA_512",
          rspAuthenticateMessage.tag
        )
      )
      _ = println(
        "Authentication tag is valid: " + rspVerifyTag.success.toString
      )
      // Verify a tag
      rspVerifyTag <- client.verifyTag(
        VerifyTagRequest(
          ByteString.copyFromUtf8(msg),
          rspGenAes.keyId,
          "HMAC_SHA_512",
          rspAuthenticateMessage.tag
        )
      )
      _ = println(
        "Authentication tag is valid: " + rspVerifyTag.success.toString
      )
    var replyVerifyTag = client.VerifyTag(
        new VerifyTagRequest { Algorithm = "HMAC_SHA_512", 
                               KeyId = keyIdAes,
                               Plaintext = ByteString.CopyFromUtf8(msgAuth),
                               Tag = replyAuthenticateMessage.Tag},
        metadata);

    Console.WriteLine("Authenticate tag is valid: " + replyVerifyTag.Success);
    var replyVerifyTagWithWrapped = client.VerifyTagWithWrapped(
        new VerifyTagWithWrappedRequest { Algorithm = "HMAC_SHA_512", 
                                          WrappedKey = respGenWrappedAes.WrappedSecret,
                                          Plaintext = ByteString.CopyFromUtf8(msgAuth),
                                          Tag = replyAuthenticateMessageWithWrapped.Tag},
        metadata);

    Console.WriteLine("Authenticate tag is valid: " + replyVerifyTagWithWrapped.Success);
  // Inside the public part of class SubtleCryptoClient
  bool VerifyTag(const std::string &algorithm, 
                 const std::string &key_id,
                 const std::string &plaintext,
                 const std::string &tag) {
    crypto::VerifyTagRequest request;
    request.set_algorithm(algorithm);
    request.set_key_id(key_id);
    request.set_plaintext(plaintext);
    request.set_tag(tag);

    crypto::VerifyTagResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->VerifyTag(&context, request, &response);

    if (status.ok()) {
      return response.success();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Verify a tag
  bool verify_tag_success =
      subtle_crypto.VerifyTag("HMAC_SHA_512", key_id_aes, tag_msg, tag);

  std::cout << "Autentication tag is valid: " 
            << std::boolalpha 
            << verify_tag_success 
            << std::endl;
  // Inside the public part of class SubtleCryptoClient
  bool VerifyTagWithWrapped(const std::string &algorithm, 
                        const std::string &wrapped_key,
                        const std::string &plaintext,
                        const std::string &tag) {
    crypto::VerifyTagWithWrappedRequest request;
    request.set_algorithm(algorithm);
    request.set_wrapped_key(wrapped_key);
    request.set_plaintext(plaintext);
    request.set_tag(tag);

    crypto::VerifyTagResponse response;
    ClientContext context;
    context.AddMetadata(HEADER, TOKEN);

    Status status = stub_->VerifyTagWithWrapped(&context, request, &response);

    if (status.ok()) {
      return response.success();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Inside of main
  // Verify a tag with wrapped key
  bool verify_tag_with_wrapped_success =
      subtle_crypto.VerifyTagWithWrapped("HMAC_SHA_512",
                                         wrapped_key_aes.wrapped_secret(),
                                         tag_msg,
                                         tag_with_wrapped);

  std::cout << "Autentication tag is valid: " 
            << std::boolalpha 
            << verify_tag_with_wrapped_success 
            << std::endl;
    // Verify a tag
    client.VerifyTag(
      {
        algorithm: 'HMAC_SHA_512',
        plaintext: Buffer.from('I want to authenticate this message', 'utf8'),
        key_id: keyIdAes,
        tag: resAuthenticateMessage.tag
      },
      metadata,
      (err, resVerifyTag) => {
        console.log(err || `Authentication tag is valid: ${resVerifyTag.success}`)
    }
  )
    // Verify a tag with wrapped key
    client.VerifyTagWithWrapped(
      {
        algorithm: 'HMAC_SHA_512',
        plaintext: Buffer.from('I want to authenticate this message', 'utf8'),
        wrapped_key: wrappedKeyAes,
        tag: resAuthenticateMessageWithWrapped.tag
      },
      metadata,
      (err, resVerifyTag) => {
        console.log(err || `Authentication tag is valid: ${resVerifyTag.success}`)
    }
  )
    // Verify a tag
    VerifyTagRequest reqVerifyTag =
        VerifyTagRequest.newBuilder()
            .setKeyId(respGenAes.getKeyId())
            .setAlgorithm("HMAC_SHA_512")
            .setPlaintext(ByteString.copyFromUtf8("I want to authenticate this message"))
            .setTag(respAuthenticateMessage.getTag())
            .build();
    
    VerifyTagResponse respVerifyTag =
        client.verifyTag(reqVerifyTag);

    System.out.println("Authentication tag is valid: " + respVerifyTag.getSuccess());
    // Verify a tag with wrapped key
    VerifyTagWithWrappedRequest reqVerifyTagWithWrapped =
        VerifyTagWithWrappedRequest.newBuilder()
            .setWrappedKey(respGenWrapAes.getWrappedSecret())
            .setAlgorithm("HMAC_SHA_512")
            .setPlaintext(ByteString.copyFromUtf8("I want to authenticate this message"))
            .setTag(respAuthenticateMessageWithWrapped.getTag())
            .build();
    
    VerifyTagResponse respVerifyTagWithWrapped =
        client.verifyTagWithWrapped(reqVerifyTagWithWrapped);

    System.out.println("Authentication tag is valid: " + respVerifyTagWithWrapped.getSuccess());

The operation of verifying a given tag consists of reproducing this tag again, and then comparing the result to the original one.

While this could be done manually by calling AuthenticateMessage again, we provide a facility that perform this comparison internally, and then returns directly the success status of the subsequent comparison. Input parameters have to be the same, with the addition of the tag be to verified.

If the key represented by the key_id does not exist, meaning its id does not exist or the key has not been generated by the currently pinned backend, or the key does not whitelist the selected algorithm, an error is returned. If all conditions are fulfilled, an authentication tag is returned.

In the response, the success field contains a boolean value reflecting the result of the verification.

Features

Here are feature matrices you can use to check if a given algorithm is supported by the cryptographic API, and through which backend.

Key generation

software-opensslhardware-dekatonhardware-utimaco
EC_SECP256K1yesyesyes
EC_SECP256R1yesyesyes
EC_SECP384R1yesyesyes
EC_SECP521R1yesyesyes
EC_ED25519yesyesyes
RSA_{2048,4096}yesyesyes
CIPHER_{128,256}yesyesyes
ECIES_{curve}yesyesno
ECDH_{curve}yesnoyes

Hashing

software-opensslhardware-dekatonhardware-utimaco
SHA_{256,384,512}yesyesyes

Signatures

software-opensslhardware-dekatonhardware-utimaco
ECDSA_{hash}yesyesyes
ECDSA_PREHASHED_{256,384,512}yesyesyes
RSASSA_NONE_{hash}yesyesyes
RSASSA_PSS_{hash}yesyesyes

Symmetric encryption

software-opensslhardware-dekatonhardware-utimaco
AES_CBC_PKCS7yesyesyes
AES_GCMyesyesyes

Derive shared secret

software-opensslhardware-dekatonhardware-utimaco
ECDHyesnoyes

Wallet key derivation

software-opensslhardware-dekatonhardware-utimaco
BIP-032yesyesno
SLIP-010yesyesno

Authentication

software-opensslhardware-dekatonhardware-utimaco
HMAC_SHA_{256,384,512}yesnoyes
HMAC_PREHASHED_{256,384,512}yesnoyes

ECIES

software-opensslhardware-dekatonhardware-utimaco
ECIES_{cipher}_{mac}_{kdf}yesyesyes

Random

software-opensslhardware-dekatonhardware-utimaco
PRNGyesnono
TRNGnoyesyes

Protocol Buffers

What are protocol buffers?

Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.

If you want to learn more about protocol buffers, checkout Google's documentation.

How do I start?

crypto/encrypt.proto

DecryptAeadRequest

Request to decrypt and authenticate data using an internal key

FieldTypeLabelDescription
ciphertextbytesData to decrypt
key_idstringKey id of the key used to decrypt the data
algorithmstringAlgorithm used to decrypt the data
ivbytesInitialization Vector. Must be empty if the algorithm don't use one
authentication_tagbytesAuthentication tag
aadbytesAdditional associated data

DecryptAeadResponse

Response with the decrypted and authenticated data

FieldTypeLabelDescription
plaintextbytesDecrypted data

DecryptEciesRequest

Request to decrypt data using an internal private key for ECIES

FieldTypeLabelDescription
algorithmstringAlgorithm specification used in encrypt
key_idstringKey id of the private key
ciphertextbytesEncrypted data
authentication_tagbytesTag of the ciphertext
ephemeral_public_keybytesEphemeral public key

DecryptEciesResponse

Response with the decrypted data

FieldTypeLabelDescription
plaintextbytesDecrypted data

DecryptRequest

Request to decrypt data using an internal key

FieldTypeLabelDescription
ciphertextbytesData to decrypt
key_idstringKey id of the key used to decrypt the data
algorithmstringAlgorithm used to decrypt the data
ivbytesInitialization Vector. Must be empty if the algorithm don't use one

DecryptResponse

Response with the decrypted data

FieldTypeLabelDescription
plaintextbytesDecrypted data

EasyDecryptRequest

Request to decrypt data using an internal key

FieldTypeLabelDescription
ciphertextbytesData to decrypt
key_idstringKey id of the key used to decrypt the data

EasyEncryptRequest

Request to encrypt data using an internal key

FieldTypeLabelDescription
plaintextbytesData to encrypt
key_idstringKey id of the key used to encrypt the data

EasyEncryptResponse

Response with the encrypted data

FieldTypeLabelDescription
ciphertextbytesEncrypted data

EncryptAeadRequest

Request to encrypt data using an internal key

FieldTypeLabelDescription
plaintextbytesData to encrypt
key_idstringKey id of the key used to encrypt the data
algorithmstringAlgorithm used to encrypt the data
ivbytesInitialization Vector. A random one in generated if none is provided. Must be empty if the algorithm don't use one
aadbytesAdditional associated data

EncryptAeadResponse

Response with the encrypted data and authentication tag

FieldTypeLabelDescription
ciphertextbytesEncrypted data
ivbytesInitialization Vector. Empty if the algorithm don't use one.
authentication_tagbytesAuthentication tag

EncryptEciesRequest

Request to encrypt data using an public key for ECIES

FieldTypeLabelDescription
algorithmstringAlgorithm specification for deriving and encrypting in ECIES
public_keybytesPublic key of destination
plaintextbytesData to encrypt

EncryptEciesResponse

Response with the encrypted data

FieldTypeLabelDescription
ciphertextbytesEncrypted data
authentication_tagbytesTag of the ciphertext
ephemeral_public_keybytesEphemeral public key

EncryptRequest

Request to encrypt data using an internal key

FieldTypeLabelDescription
plaintextbytesData to encrypt
key_idstringKey id of the key used to encrypt the data
algorithmstringAlgorithm used to encrypt the data
ivbytesInitialization Vector. A random one in generated if none is provided. Must be empty if the algorithm don't use one

EncryptResponse

Response with the encrypted data

FieldTypeLabelDescription
ciphertextbytesEncrypted data
ivbytesInitialization Vector. Empty if the algorithm don't use one.

PublicDecryptRequest

Request to decrypt data using an internal key

FieldTypeLabelDescription
ciphertextbytesData to decrypt
key_idstringKey id of the key used to decrypt the data
algorithmstringAlgorithm used to decrypt the data

PublicDecryptResponse

Response with the decrypted data

FieldTypeLabelDescription
plaintextbytesDecrypted data

PublicEncryptRequest

Request to encrypt data using a public key

FieldTypeLabelDescription
plaintextbytesData to encrypt
public_keybytesPublic key used to encrypt the data
algorithmstringAlgorithm used to encrypt the data

PublicEncryptResponse

Response with the encrypted data

FieldTypeLabelDescription
ciphertextbytesEncrypted data

crypto/generate.proto

EasyGenerateEncryptionKeyRequest

Request to generate cryptographic material for encryption operations

FieldTypeLabelDescription
delete_afterstringDuration after which the key will be automatically deleted

EasyGenerateEncryptionKeyResponse

Response with the id of the generated material

FieldTypeLabelDescription
key_idstringId of the generated key

EasyGenerateSignatureKeyRequest

Request to generate cryptographic material for signature operations

FieldTypeLabelDescription
delete_afterstringDuration after which the key will be automatically deleted

EasyGenerateSignatureKeyResponse

Response with the id of the generated material

FieldTypeLabelDescription
privatestringId of the generated key
publicbytesEncoded public key generated

GenerateKeyRequest

Request to generate cryptographic material

FieldTypeLabelDescription
algorithmstringAlgorithm used to generate the key
whiteliststringrepeatedWhitelist of algorithms this key will be able to use
delete_afterstringDuration after which the key will be automatically deleted

GenerateKeyResponse

Response with the id of the generated material

FieldTypeLabelDescription
key_idstringId of the generated key

GenerateWrappedKeyRequest

Request to generate wrapped cryptographic material

FieldTypeLabelDescription
algorithmstringAlgorithm used to generate the key

GenerateWrappedKeyResponse

Request to generate wrapped cryptographic material

FieldTypeLabelDescription
wrapped_secretbytesWrapped secret key generated
publicbytesEncoded public key generated for asym algorithm

RevokeKeyRequest

Request to revoke cryptographic material

FieldTypeLabelDescription
key_idstringId of the generated key

RevokeKeyResponse

Response to revoke request

crypto/hash.proto

HashRequest

Request to hash data

FieldTypeLabelDescription
databytesData to hash
algorithmstringAlgorithm used to hash the data

HashResponse

Response with the digest

FieldTypeLabelDescription
digestbytesHashed data

crypto/key_exchange.proto

DeriveEcdhRequest

Request to derive secret with ECDH algorithm

FieldTypeLabelDescription
key_idstringsecret key id
public_keybytesPublic key from the opposite side

DeriveEcdhResponse

Response to derive secret with ECDH algorithm

FieldTypeLabelDescription
shared_secretbytesShared secret

DeriveEcdhWithWrappedRequest

Request to derive secret with ECDH algorithm with a wrapped secret

FieldTypeLabelDescription
wrapped_secretbytessecret wrapped key
public_keybytesPublic key from the opposite side

crypto/mac.proto

AuthenticateMessageRequest

Request to authenticate data using an internal key

FieldTypeLabelDescription
plaintextbytesData to authenticate
key_idstringKey used to authenticate the data
algorithmstringAlgorithm used to hash the data

AuthenticateMessageResponse

Response with the encrypted data

FieldTypeLabelDescription
tagbytesEncrypted data

AuthenticateMessageWithWrappedRequest

Request to authenticate data using an internal key

FieldTypeLabelDescription
plaintextbytesData to authenticate
wrapped_keybytesWrapped key used to authenticate the data
algorithmstringAlgorithm used to hash the data

VerifyTagRequest

Request to verify authenticated data using an internal key

FieldTypeLabelDescription
plaintextbytesData to authenticate
key_idstringKey used to authenticate the data
algorithmstringMac algorithm used to authenticate the data
tagbytesTag to verify

VerifyTagResponse

Response with the success status

FieldTypeLabelDescription
successboolSuccess status of the verification

VerifyTagWithWrappedRequest

Request to verify authenticated data using an internal key

FieldTypeLabelDescription
plaintextbytesData to authenticate
wrapped_keybytesWrapped key used to authenticate the data
algorithmstringMac algorithm used to authenticate the data
tagbytesTag to verify

crypto/public.proto

GetPublicComponentRequest

Request to get the public component of an internal key

FieldTypeLabelDescription
key_idstringKey id of the public key to fetch

GetPublicComponentResponse

Response with the public data

FieldTypeLabelDescription
publicbytesPublic key data

crypto/rand.proto

GetEntropyRequest

Request random bytes in a buffer of given length

FieldTypeLabelDescription
lengthuint32Entropy buffer length in bytes
sourcestringSource of the entropy

GetEntropyResponse

Response with the requested entropy in a buffer

FieldTypeLabelDescription
entropybytesEntropy buffer

crypto/sign.proto

EasySignRequest

Request to sign data using an internal key

FieldTypeLabelDescription
databytesData to sign
key_idstringKey id of the key used to sign the data

EasyVerifySignatureRequest

Request to verify data with a public key

FieldTypeLabelDescription
databytesData to verify
signaturebytesSignature of the data
keybytesPublic key used to verify the data

SignRequest

Request to sign data using an internal key

FieldTypeLabelDescription
databytesData to sign
key_idstringKey id of the key used to sign the data
algorithmstringAlgorithm used to sign the data

SignResponse

Response with the requested signature

FieldTypeLabelDescription
signaturebytesSignature of the data

SignWithWrappedRequest

Request to sign data using a wrapped key

FieldTypeLabelDescription
databytesData to sign
wrapped_keybytesWrapped key used to sign the data
algorithmstringAlgorithm used to sign the data

VerifySignatureRequest

Request to verify data with a public key

FieldTypeLabelDescription
databytesData to verify
signaturebytesSignature of the data
keybytesPublic key used to verify the data
algorithmstringAlgorithm used to verify the data

VerifySignatureResponse

Response with the validity of the signature

FieldTypeLabelDescription
validboolValidity of the signature

crypto/wallet.proto

DeriveWalletAndSignOneShotRequest

One-shot requests that derive and use child_key from seed&path directly

FieldTypeLabelDescription
algorithmstringAlgorithm used to derive the master key
plaintextbytesPlaintext seed used to derive the master key
wrappedbytesWrapped seed used to derive the master key
pathuint32repeatedPath of the child
databytesData to sign
signature_algorithmstringAlgorithm used to sign the data

DeriveWalletAndSignOneShotResponse

FieldTypeLabelDescription
signaturebytesSignature of the data

DeriveWalletMasterKeyRequest

FieldTypeLabelDescription
algorithmstringAlgorithm used to derive the master key
whiteliststringrepeatedWhitelist of algorithms this key will be able to use
plaintextbytesPlaintext seed used to derive the master key
wrappedbytesWrapped seed used to derive the master key

DeriveWalletMasterKeyResponse

FieldTypeLabelDescription
key_idstringId of the derived key. The key include the chain code

DeriveWalletPrivateKeyRequest

FieldTypeLabelDescription
whiteliststringrepeatedWhitelist of algorithms the child key will be able to use. Must be a subset of the parent key whitelist
key_idstringId of the parent key
pathuint32repeatedPath of the child

DeriveWalletPrivateKeyResponse

FieldTypeLabelDescription
key_idstringId of the derived child key. The key include the chain code

DeriveWalletPublicKeyOneShotRequest

FieldTypeLabelDescription
algorithmstringAlgorithm used to derive the master key
plaintextbytesPlaintext seed used to derive the master key
wrappedbytesWrapped seed used to derive the master key
pathuint32repeatedPath of the child

DeriveWalletPublicKeyOneShotResponse

FieldTypeLabelDescription
public_keybytesPublic key of the child
chain_codebytesChain code of the child

DeriveWalletPublicKeyRequest

FieldTypeLabelDescription
algorithmstringAlgorithm used to derive the child key
public_keybytesPublic key of the parent
chain_codebytesChain code of the parent
pathuint32repeatedPath of the child

DeriveWalletPublicKeyResponse

FieldTypeLabelDescription
public_keybytesPublic key of the child
chain_codebytesChain code of the child

DeriveWalletSecretKeyOneShotRequest

FieldTypeLabelDescription
algorithmstringAlgorithm used to derive the master key
plaintextbytesPlaintext seed used to derive the master key
wrappedbytesWrapped seed used to derive the master key
pathuint32repeatedPath of the child

DeriveWalletSecretKeyOneShotResponse

FieldTypeLabelDescription
secret_keybytesSecret key of the child
public_keybytesPublic key of the child
chain_codebytesChain code of the child

GenerateWalletSeedRequest

FieldTypeLabelDescription
lengthuint32Length of the seed to generate in bytes [16, 64]

GenerateWalletSeedResponse

FieldTypeLabelDescription
wrapped_seedbytesWrapped seed bytes

GetWalletPublicComponentsRequest

Request to get the public components of a wallet extended public key

FieldTypeLabelDescription
key_idstringKey id of the public key to fetch

GetWalletPublicComponentsResponse

FieldTypeLabelDescription
publicbytesPublic key data
chain_codebytesChain code data
parent_fingerprintbytesFingerprint of the parent public key, 0x00000000 for a master key
indexuint32Index used to derive this key, 0 for a master key
depthuint32Depth of the key in the tree

ImportArca1WalletSeedRequest

Request to import a transport key wrapped ARCA1 wallet seed

FieldTypeLabelDescription
arca1_seedbytesTransport wrapped seed bytes
transport_keybytesTransport key bytes

ImportArca1WalletSeedResponse

FieldTypeLabelDescription
wrapped_seedbytesRe-wrapped seed bytes

service/crypto.proto

RPC services declaration

Service providing high-level cryptographic capabilities

Method NameRequest TypeResponse TypeDescription
SignEasySignRequestSignResponseSign messages
VerifyEasyVerifySignatureRequestVerifySignatureResponseVerify signatures
EncryptEasyEncryptRequestEasyEncryptResponseEasy symmetric encryption with authentication
DecryptEasyDecryptRequestDecryptResponseEasy symmetric decryption with authentication
GenerateEncryptionKeyEasyGenerateEncryptionKeyRequestEasyGenerateEncryptionKeyResponseGenerate symmetric key material
GenerateSignatureKeyEasyGenerateSignatureKeyRequestEasyGenerateSignatureKeyResponseGenerate asym key material

Service providing low-level cryptographic capabilities

Method NameRequest TypeResponse TypeDescription
GetEntropyGetEntropyRequestGetEntropyResponseGet random bytes
HashHashRequestHashResponseHash data
AuthenticateMessageAuthenticateMessageRequestAuthenticateMessageResponseAuthenticate a message
VerifyTagVerifyTagRequestVerifyTagResponseVerify a message authentication code
AuthenticateMessageWithWrappedAuthenticateMessageWithWrappedRequestAuthenticateMessageResponseAuthenticate a message using a wrapped key
VerifyTagWithWrappedVerifyTagWithWrappedRequestVerifyTagResponseVerify a message authentication code using a wrapped key
GenerateKeyGenerateKeyRequestGenerateKeyResponseGenerate key material
GenerateWrappedKeyGenerateWrappedKeyRequestGenerateWrappedKeyResponseGenerate key material and export it
RevokeKeyRevokeKeyRequestRevokeKeyResponseRevoke key material
GetPublicComponentGetPublicComponentRequestGetPublicComponentResponseGet the public component of some key material
SignSignRequestSignResponseSign messages
SignWithWrappedSignWithWrappedRequestSignResponseSign messages using wrapped key
VerifySignatureVerifySignatureRequestVerifySignatureResponseVerify signatures
EncryptEncryptRequestEncryptResponseSymmetric encryption without authentication
DecryptDecryptRequestDecryptResponseSymmetric decryption without authentication
EncryptAeadEncryptAeadRequestEncryptAeadResponseSymmetric encryption and decryption with authentication and additional
associated data
DecryptAeadDecryptAeadRequestDecryptAeadResponseSymmetric decryption with authentication and additional
associated data
EncryptEciesEncryptEciesRequestEncryptEciesResponseEncryption using Elliptic Curve Integrated Encryption Scheme (ECIES)
DecryptEciesDecryptEciesRequestDecryptEciesResponseDecryption using Elliptic Curve Integrated Encryption Scheme (ECIES)
PublicEncryptPublicEncryptRequestPublicEncryptResponsePublic encryption without authentication
PublicDecryptPublicDecryptRequestPublicDecryptResponsePublic decryption without authentication
GenerateWalletSeedGenerateWalletSeedRequestGenerateWalletSeedResponseGenerate a wallet seed
DeriveWalletMasterKeyDeriveWalletMasterKeyRequestDeriveWalletMasterKeyResponseDerive a wallet master key from seed
DeriveWalletPrivateKeyDeriveWalletPrivateKeyRequestDeriveWalletPrivateKeyResponseDerive a wallet child private key from master secret key and path
DeriveWalletPublicKeyDeriveWalletPublicKeyRequestDeriveWalletPublicKeyResponseDerive a wallet child public key from master public key and path
DeriveWalletAndSignOneShotDeriveWalletAndSignOneShotRequestDeriveWalletAndSignOneShotResponseDerive a wallet child directly from seed and path
and sign the given payload with it
DeriveWalletSecretKeyOneShotDeriveWalletSecretKeyOneShotRequestDeriveWalletSecretKeyOneShotResponseDerive a wallet child key pair directly from seed and path
DeriveWalletPublicKeyOneShotDeriveWalletPublicKeyOneShotRequestDeriveWalletPublicKeyOneShotResponseDerive a wallet child public key directly from seed and path
GetWalletPublicComponentsGetWalletPublicComponentsRequestGetWalletPublicComponentsResponseGet the wallet child public key and chain code corresponding to the
given child private key
ImportArca1WalletSeedImportArca1WalletSeedRequestImportArca1WalletSeedResponseImport an ARCA1 wallet seed wrapped with a the given transport key
DeriveEcdhDeriveEcdhRequestDeriveEcdhResponseDerive a secret using Elliptic Curve Diffie-Hellman
DeriveEcdhWithWrappedDeriveEcdhWithWrappedRequestDeriveEcdhResponseDerive a secret using Elliptic Curve Diffie-Hellman using a wrapped key