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:
- Streaming, both unidirectional and bidirectional
- Cancellation
- Timeouts
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:
software-openssl
hardware-dekaton
hardware-utimaco
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:
"TRNG"
, True Random Number Generator"BLAKE2_512"
,Blake2
in512
bits mode"RSAES_OAEP"
,RSA
cipher withOAEP
padding scheme"AES_CTR"
,AES
withCTR
block cipher mode"RSA_4096"
,RSA
key of4096
modulus bits size
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:
PRNG
, with a Pseudo Random Number Generator, i.e. software based random numbersTRNG
, with a True Random Number Generator, i.e. with an physical device that generate random numbersQRNG
, with a Quantum Random Number Generator, i.e. with a device using quantum mechanics to generate random numbers
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:
- Generating a master key
- Getting public components of keys
- Deriving secret keys
- Deriving public keys
- Generate and Derive public key
- Generate, Derive and Sign
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-openssl | hardware-dekaton | hardware-utimaco | |
---|---|---|---|
EC_SECP256K1 | yes | yes | yes |
EC_SECP256R1 | yes | yes | yes |
EC_SECP384R1 | yes | yes | yes |
EC_SECP521R1 | yes | yes | yes |
EC_ED25519 | yes | yes | yes |
RSA_{2048,4096} | yes | yes | yes |
CIPHER_{128,256} | yes | yes | yes |
ECIES_{curve} | yes | yes | no |
ECDH_{curve} | yes | no | yes |
Hashing
software-openssl | hardware-dekaton | hardware-utimaco | |
---|---|---|---|
SHA_{256,384,512} | yes | yes | yes |
Signatures
software-openssl | hardware-dekaton | hardware-utimaco | |
---|---|---|---|
ECDSA_{hash} | yes | yes | yes |
ECDSA_PREHASHED_{256,384,512} | yes | yes | yes |
RSASSA_NONE_{hash} | yes | yes | yes |
RSASSA_PSS_{hash} | yes | yes | yes |
Symmetric encryption
software-openssl | hardware-dekaton | hardware-utimaco | |
---|---|---|---|
AES_CBC_PKCS7 | yes | yes | yes |
AES_GCM | yes | yes | yes |
Derive shared secret
software-openssl | hardware-dekaton | hardware-utimaco | |
---|---|---|---|
ECDH | yes | no | yes |
Wallet key derivation
software-openssl | hardware-dekaton | hardware-utimaco | |
---|---|---|---|
BIP-032 | yes | yes | no |
SLIP-010 | yes | yes | no |
Authentication
software-openssl | hardware-dekaton | hardware-utimaco | |
---|---|---|---|
HMAC_SHA_{256,384,512} | yes | no | yes |
HMAC_PREHASHED_{256,384,512} | yes | no | yes |
ECIES
software-openssl | hardware-dekaton | hardware-utimaco | |
---|---|---|---|
ECIES_{cipher}_{mac}_{kdf} | yes | yes | yes |
Random
software-openssl | hardware-dekaton | hardware-utimaco | |
---|---|---|---|
PRNG | yes | no | no |
TRNG | no | yes | yes |
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?
- Download and install the protocol buffer compiler.
- Read the overview.
- Try the tutorial for your chosen language.
crypto/encrypt.proto
DecryptAeadRequest
Request to decrypt and authenticate data using an internal key
Field | Type | Label | Description |
---|---|---|---|
ciphertext | bytes | Data to decrypt | |
key_id | string | Key id of the key used to decrypt the data | |
algorithm | string | Algorithm used to decrypt the data | |
iv | bytes | Initialization Vector. Must be empty if the algorithm don't use one | |
authentication_tag | bytes | Authentication tag | |
aad | bytes | Additional associated data |
DecryptAeadResponse
Response with the decrypted and authenticated data
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Decrypted data |
DecryptEciesRequest
Request to decrypt data using an internal private key for ECIES
Field | Type | Label | Description |
---|---|---|---|
algorithm | string | Algorithm specification used in encrypt | |
key_id | string | Key id of the private key | |
ciphertext | bytes | Encrypted data | |
authentication_tag | bytes | Tag of the ciphertext | |
ephemeral_public_key | bytes | Ephemeral public key |
DecryptEciesResponse
Response with the decrypted data
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Decrypted data |
DecryptRequest
Request to decrypt data using an internal key
Field | Type | Label | Description |
---|---|---|---|
ciphertext | bytes | Data to decrypt | |
key_id | string | Key id of the key used to decrypt the data | |
algorithm | string | Algorithm used to decrypt the data | |
iv | bytes | Initialization Vector. Must be empty if the algorithm don't use one |
DecryptResponse
Response with the decrypted data
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Decrypted data |
EasyDecryptRequest
Request to decrypt data using an internal key
Field | Type | Label | Description |
---|---|---|---|
ciphertext | bytes | Data to decrypt | |
key_id | string | Key id of the key used to decrypt the data |
EasyEncryptRequest
Request to encrypt data using an internal key
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Data to encrypt | |
key_id | string | Key id of the key used to encrypt the data |
EasyEncryptResponse
Response with the encrypted data
Field | Type | Label | Description |
---|---|---|---|
ciphertext | bytes | Encrypted data |
EncryptAeadRequest
Request to encrypt data using an internal key
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Data to encrypt | |
key_id | string | Key id of the key used to encrypt the data | |
algorithm | string | Algorithm used to encrypt the data | |
iv | bytes | Initialization Vector. A random one in generated if none is provided. Must be empty if the algorithm don't use one | |
aad | bytes | Additional associated data |
EncryptAeadResponse
Response with the encrypted data and authentication tag
Field | Type | Label | Description |
---|---|---|---|
ciphertext | bytes | Encrypted data | |
iv | bytes | Initialization Vector. Empty if the algorithm don't use one. | |
authentication_tag | bytes | Authentication tag |
EncryptEciesRequest
Request to encrypt data using an public key for ECIES
Field | Type | Label | Description |
---|---|---|---|
algorithm | string | Algorithm specification for deriving and encrypting in ECIES | |
public_key | bytes | Public key of destination | |
plaintext | bytes | Data to encrypt |
EncryptEciesResponse
Response with the encrypted data
Field | Type | Label | Description |
---|---|---|---|
ciphertext | bytes | Encrypted data | |
authentication_tag | bytes | Tag of the ciphertext | |
ephemeral_public_key | bytes | Ephemeral public key |
EncryptRequest
Request to encrypt data using an internal key
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Data to encrypt | |
key_id | string | Key id of the key used to encrypt the data | |
algorithm | string | Algorithm used to encrypt the data | |
iv | bytes | Initialization 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
Field | Type | Label | Description |
---|---|---|---|
ciphertext | bytes | Encrypted data | |
iv | bytes | Initialization Vector. Empty if the algorithm don't use one. |
PublicDecryptRequest
Request to decrypt data using an internal key
Field | Type | Label | Description |
---|---|---|---|
ciphertext | bytes | Data to decrypt | |
key_id | string | Key id of the key used to decrypt the data | |
algorithm | string | Algorithm used to decrypt the data |
PublicDecryptResponse
Response with the decrypted data
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Decrypted data |
PublicEncryptRequest
Request to encrypt data using a public key
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Data to encrypt | |
public_key | bytes | Public key used to encrypt the data | |
algorithm | string | Algorithm used to encrypt the data |
PublicEncryptResponse
Response with the encrypted data
Field | Type | Label | Description |
---|---|---|---|
ciphertext | bytes | Encrypted data |
crypto/generate.proto
EasyGenerateEncryptionKeyRequest
Request to generate cryptographic material for encryption operations
Field | Type | Label | Description |
---|---|---|---|
delete_after | string | Duration after which the key will be automatically deleted |
EasyGenerateEncryptionKeyResponse
Response with the id of the generated material
Field | Type | Label | Description |
---|---|---|---|
key_id | string | Id of the generated key |
EasyGenerateSignatureKeyRequest
Request to generate cryptographic material for signature operations
Field | Type | Label | Description |
---|---|---|---|
delete_after | string | Duration after which the key will be automatically deleted |
EasyGenerateSignatureKeyResponse
Response with the id of the generated material
Field | Type | Label | Description |
---|---|---|---|
private | string | Id of the generated key | |
public | bytes | Encoded public key generated |
GenerateKeyRequest
Request to generate cryptographic material
Field | Type | Label | Description |
---|---|---|---|
algorithm | string | Algorithm used to generate the key | |
whitelist | string | repeated | Whitelist of algorithms this key will be able to use |
delete_after | string | Duration after which the key will be automatically deleted |
GenerateKeyResponse
Response with the id of the generated material
Field | Type | Label | Description |
---|---|---|---|
key_id | string | Id of the generated key |
GenerateWrappedKeyRequest
Request to generate wrapped cryptographic material
Field | Type | Label | Description |
---|---|---|---|
algorithm | string | Algorithm used to generate the key |
GenerateWrappedKeyResponse
Request to generate wrapped cryptographic material
Field | Type | Label | Description |
---|---|---|---|
wrapped_secret | bytes | Wrapped secret key generated | |
public | bytes | Encoded public key generated for asym algorithm |
RevokeKeyRequest
Request to revoke cryptographic material
Field | Type | Label | Description |
---|---|---|---|
key_id | string | Id of the generated key |
RevokeKeyResponse
Response to revoke request
crypto/hash.proto
HashRequest
Request to hash data
Field | Type | Label | Description |
---|---|---|---|
data | bytes | Data to hash | |
algorithm | string | Algorithm used to hash the data |
HashResponse
Response with the digest
Field | Type | Label | Description |
---|---|---|---|
digest | bytes | Hashed data |
crypto/key_exchange.proto
DeriveEcdhRequest
Request to derive secret with ECDH algorithm
Field | Type | Label | Description |
---|---|---|---|
key_id | string | secret key id | |
public_key | bytes | Public key from the opposite side |
DeriveEcdhResponse
Response to derive secret with ECDH algorithm
Field | Type | Label | Description |
---|---|---|---|
shared_secret | bytes | Shared secret |
DeriveEcdhWithWrappedRequest
Request to derive secret with ECDH algorithm with a wrapped secret
Field | Type | Label | Description |
---|---|---|---|
wrapped_secret | bytes | secret wrapped key | |
public_key | bytes | Public key from the opposite side |
crypto/mac.proto
AuthenticateMessageRequest
Request to authenticate data using an internal key
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Data to authenticate | |
key_id | string | Key used to authenticate the data | |
algorithm | string | Algorithm used to hash the data |
AuthenticateMessageResponse
Response with the encrypted data
Field | Type | Label | Description |
---|---|---|---|
tag | bytes | Encrypted data |
AuthenticateMessageWithWrappedRequest
Request to authenticate data using an internal key
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Data to authenticate | |
wrapped_key | bytes | Wrapped key used to authenticate the data | |
algorithm | string | Algorithm used to hash the data |
VerifyTagRequest
Request to verify authenticated data using an internal key
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Data to authenticate | |
key_id | string | Key used to authenticate the data | |
algorithm | string | Mac algorithm used to authenticate the data | |
tag | bytes | Tag to verify |
VerifyTagResponse
Response with the success status
Field | Type | Label | Description |
---|---|---|---|
success | bool | Success status of the verification |
VerifyTagWithWrappedRequest
Request to verify authenticated data using an internal key
Field | Type | Label | Description |
---|---|---|---|
plaintext | bytes | Data to authenticate | |
wrapped_key | bytes | Wrapped key used to authenticate the data | |
algorithm | string | Mac algorithm used to authenticate the data | |
tag | bytes | Tag to verify |
crypto/public.proto
GetPublicComponentRequest
Request to get the public component of an internal key
Field | Type | Label | Description |
---|---|---|---|
key_id | string | Key id of the public key to fetch |
GetPublicComponentResponse
Response with the public data
Field | Type | Label | Description |
---|---|---|---|
public | bytes | Public key data |
crypto/rand.proto
GetEntropyRequest
Request random bytes in a buffer of given length
Field | Type | Label | Description |
---|---|---|---|
length | uint32 | Entropy buffer length in bytes | |
source | string | Source of the entropy |
GetEntropyResponse
Response with the requested entropy in a buffer
Field | Type | Label | Description |
---|---|---|---|
entropy | bytes | Entropy buffer |
crypto/sign.proto
EasySignRequest
Request to sign data using an internal key
Field | Type | Label | Description |
---|---|---|---|
data | bytes | Data to sign | |
key_id | string | Key id of the key used to sign the data |
EasyVerifySignatureRequest
Request to verify data with a public key
Field | Type | Label | Description |
---|---|---|---|
data | bytes | Data to verify | |
signature | bytes | Signature of the data | |
key | bytes | Public key used to verify the data |
SignRequest
Request to sign data using an internal key
Field | Type | Label | Description |
---|---|---|---|
data | bytes | Data to sign | |
key_id | string | Key id of the key used to sign the data | |
algorithm | string | Algorithm used to sign the data |
SignResponse
Response with the requested signature
Field | Type | Label | Description |
---|---|---|---|
signature | bytes | Signature of the data |
SignWithWrappedRequest
Request to sign data using a wrapped key
Field | Type | Label | Description |
---|---|---|---|
data | bytes | Data to sign | |
wrapped_key | bytes | Wrapped key used to sign the data | |
algorithm | string | Algorithm used to sign the data |
VerifySignatureRequest
Request to verify data with a public key
Field | Type | Label | Description |
---|---|---|---|
data | bytes | Data to verify | |
signature | bytes | Signature of the data | |
key | bytes | Public key used to verify the data | |
algorithm | string | Algorithm used to verify the data |
VerifySignatureResponse
Response with the validity of the signature
Field | Type | Label | Description |
---|---|---|---|
valid | bool | Validity of the signature |
crypto/wallet.proto
DeriveWalletAndSignOneShotRequest
One-shot requests that derive and use child_key from seed&path directly
Field | Type | Label | Description |
---|---|---|---|
algorithm | string | Algorithm used to derive the master key | |
plaintext | bytes | Plaintext seed used to derive the master key | |
wrapped | bytes | Wrapped seed used to derive the master key | |
path | uint32 | repeated | Path of the child |
data | bytes | Data to sign | |
signature_algorithm | string | Algorithm used to sign the data |
DeriveWalletAndSignOneShotResponse
Field | Type | Label | Description |
---|---|---|---|
signature | bytes | Signature of the data |
DeriveWalletMasterKeyRequest
Field | Type | Label | Description |
---|---|---|---|
algorithm | string | Algorithm used to derive the master key | |
whitelist | string | repeated | Whitelist of algorithms this key will be able to use |
plaintext | bytes | Plaintext seed used to derive the master key | |
wrapped | bytes | Wrapped seed used to derive the master key |
DeriveWalletMasterKeyResponse
Field | Type | Label | Description |
---|---|---|---|
key_id | string | Id of the derived key. The key include the chain code |
DeriveWalletPrivateKeyRequest
Field | Type | Label | Description |
---|---|---|---|
whitelist | string | repeated | Whitelist of algorithms the child key will be able to use. Must be a subset of the parent key whitelist |
key_id | string | Id of the parent key | |
path | uint32 | repeated | Path of the child |
DeriveWalletPrivateKeyResponse
Field | Type | Label | Description |
---|---|---|---|
key_id | string | Id of the derived child key. The key include the chain code |
DeriveWalletPublicKeyOneShotRequest
Field | Type | Label | Description |
---|---|---|---|
algorithm | string | Algorithm used to derive the master key | |
plaintext | bytes | Plaintext seed used to derive the master key | |
wrapped | bytes | Wrapped seed used to derive the master key | |
path | uint32 | repeated | Path of the child |
DeriveWalletPublicKeyOneShotResponse
Field | Type | Label | Description |
---|---|---|---|
public_key | bytes | Public key of the child | |
chain_code | bytes | Chain code of the child |
DeriveWalletPublicKeyRequest
Field | Type | Label | Description |
---|---|---|---|
algorithm | string | Algorithm used to derive the child key | |
public_key | bytes | Public key of the parent | |
chain_code | bytes | Chain code of the parent | |
path | uint32 | repeated | Path of the child |
DeriveWalletPublicKeyResponse
Field | Type | Label | Description |
---|---|---|---|
public_key | bytes | Public key of the child | |
chain_code | bytes | Chain code of the child |
DeriveWalletSecretKeyOneShotRequest
Field | Type | Label | Description |
---|---|---|---|
algorithm | string | Algorithm used to derive the master key | |
plaintext | bytes | Plaintext seed used to derive the master key | |
wrapped | bytes | Wrapped seed used to derive the master key | |
path | uint32 | repeated | Path of the child |
DeriveWalletSecretKeyOneShotResponse
Field | Type | Label | Description |
---|---|---|---|
secret_key | bytes | Secret key of the child | |
public_key | bytes | Public key of the child | |
chain_code | bytes | Chain code of the child |
GenerateWalletSeedRequest
Field | Type | Label | Description |
---|---|---|---|
length | uint32 | Length of the seed to generate in bytes [16, 64] |
GenerateWalletSeedResponse
Field | Type | Label | Description |
---|---|---|---|
wrapped_seed | bytes | Wrapped seed bytes |
GetWalletPublicComponentsRequest
Request to get the public components of a wallet extended public key
Field | Type | Label | Description |
---|---|---|---|
key_id | string | Key id of the public key to fetch |
GetWalletPublicComponentsResponse
Field | Type | Label | Description |
---|---|---|---|
public | bytes | Public key data | |
chain_code | bytes | Chain code data | |
parent_fingerprint | bytes | Fingerprint of the parent public key, 0x00000000 for a master key | |
index | uint32 | Index used to derive this key, 0 for a master key | |
depth | uint32 | Depth of the key in the tree |
ImportArca1WalletSeedRequest
Request to import a transport key wrapped ARCA1 wallet seed
Field | Type | Label | Description |
---|---|---|---|
arca1_seed | bytes | Transport wrapped seed bytes | |
transport_key | bytes | Transport key bytes |
ImportArca1WalletSeedResponse
Field | Type | Label | Description |
---|---|---|---|
wrapped_seed | bytes | Re-wrapped seed bytes |
service/crypto.proto
RPC services declaration
Service providing high-level cryptographic capabilities
Method Name | Request Type | Response Type | Description |
---|---|---|---|
Sign | EasySignRequest | SignResponse | Sign messages |
Verify | EasyVerifySignatureRequest | VerifySignatureResponse | Verify signatures |
Encrypt | EasyEncryptRequest | EasyEncryptResponse | Easy symmetric encryption with authentication |
Decrypt | EasyDecryptRequest | DecryptResponse | Easy symmetric decryption with authentication |
GenerateEncryptionKey | EasyGenerateEncryptionKeyRequest | EasyGenerateEncryptionKeyResponse | Generate symmetric key material |
GenerateSignatureKey | EasyGenerateSignatureKeyRequest | EasyGenerateSignatureKeyResponse | Generate asym key material |
Service providing low-level cryptographic capabilities
Method Name | Request Type | Response Type | Description |
---|---|---|---|
GetEntropy | GetEntropyRequest | GetEntropyResponse | Get random bytes |
Hash | HashRequest | HashResponse | Hash data |
AuthenticateMessage | AuthenticateMessageRequest | AuthenticateMessageResponse | Authenticate a message |
VerifyTag | VerifyTagRequest | VerifyTagResponse | Verify a message authentication code |
AuthenticateMessageWithWrapped | AuthenticateMessageWithWrappedRequest | AuthenticateMessageResponse | Authenticate a message using a wrapped key |
VerifyTagWithWrapped | VerifyTagWithWrappedRequest | VerifyTagResponse | Verify a message authentication code using a wrapped key |
GenerateKey | GenerateKeyRequest | GenerateKeyResponse | Generate key material |
GenerateWrappedKey | GenerateWrappedKeyRequest | GenerateWrappedKeyResponse | Generate key material and export it |
RevokeKey | RevokeKeyRequest | RevokeKeyResponse | Revoke key material |
GetPublicComponent | GetPublicComponentRequest | GetPublicComponentResponse | Get the public component of some key material |
Sign | SignRequest | SignResponse | Sign messages |
SignWithWrapped | SignWithWrappedRequest | SignResponse | Sign messages using wrapped key |
VerifySignature | VerifySignatureRequest | VerifySignatureResponse | Verify signatures |
Encrypt | EncryptRequest | EncryptResponse | Symmetric encryption without authentication |
Decrypt | DecryptRequest | DecryptResponse | Symmetric decryption without authentication |
EncryptAead | EncryptAeadRequest | EncryptAeadResponse | Symmetric encryption and decryption with authentication and additional |
associated data | |||
DecryptAead | DecryptAeadRequest | DecryptAeadResponse | Symmetric decryption with authentication and additional |
associated data | |||
EncryptEcies | EncryptEciesRequest | EncryptEciesResponse | Encryption using Elliptic Curve Integrated Encryption Scheme (ECIES) |
DecryptEcies | DecryptEciesRequest | DecryptEciesResponse | Decryption using Elliptic Curve Integrated Encryption Scheme (ECIES) |
PublicEncrypt | PublicEncryptRequest | PublicEncryptResponse | Public encryption without authentication |
PublicDecrypt | PublicDecryptRequest | PublicDecryptResponse | Public decryption without authentication |
GenerateWalletSeed | GenerateWalletSeedRequest | GenerateWalletSeedResponse | Generate a wallet seed |
DeriveWalletMasterKey | DeriveWalletMasterKeyRequest | DeriveWalletMasterKeyResponse | Derive a wallet master key from seed |
DeriveWalletPrivateKey | DeriveWalletPrivateKeyRequest | DeriveWalletPrivateKeyResponse | Derive a wallet child private key from master secret key and path |
DeriveWalletPublicKey | DeriveWalletPublicKeyRequest | DeriveWalletPublicKeyResponse | Derive a wallet child public key from master public key and path |
DeriveWalletAndSignOneShot | DeriveWalletAndSignOneShotRequest | DeriveWalletAndSignOneShotResponse | Derive a wallet child directly from seed and path |
and sign the given payload with it | |||
DeriveWalletSecretKeyOneShot | DeriveWalletSecretKeyOneShotRequest | DeriveWalletSecretKeyOneShotResponse | Derive a wallet child key pair directly from seed and path |
DeriveWalletPublicKeyOneShot | DeriveWalletPublicKeyOneShotRequest | DeriveWalletPublicKeyOneShotResponse | Derive a wallet child public key directly from seed and path |
GetWalletPublicComponents | GetWalletPublicComponentsRequest | GetWalletPublicComponentsResponse | Get the wallet child public key and chain code corresponding to the |
given child private key | |||
ImportArca1WalletSeed | ImportArca1WalletSeedRequest | ImportArca1WalletSeedResponse | Import an ARCA1 wallet seed wrapped with a the given transport key |
DeriveEcdh | DeriveEcdhRequest | DeriveEcdhResponse | Derive a secret using Elliptic Curve Diffie-Hellman |
DeriveEcdhWithWrapped | DeriveEcdhWithWrappedRequest | DeriveEcdhResponse | Derive a secret using Elliptic Curve Diffie-Hellman using a wrapped key |