Итак, я попытался экспортировать закрытый ключ из YubiHSM2. Поэтому я импортировал пользовательский wrap-ключ с 16 байтами, сгенерировал пару ключей ed25519 и экспортировал его с импортированным wrapkey
. Я использовал библиотеку yubihsm2 c для использования интерфейса, обернул ее в go и зашифровал с помощью go -package aesccm (вы увидите ссылки на пакеты в коде) и попытался подписать и проверить с помощью libhydrogen:
main. go:
package main
import (
"fmt"
yhsm2_k "github.com/Pymann/yubihsm2-key"
aes_ccm "github.com/pschlump/AesCCM"
"crypto/aes"
hydro "github.com/someburner/libhydrogen-go"
)
/**
func YH_generate_ed25519_keypair( authkey uint16
password,
label,
domains string,
wrapkey []byte,
sk []byte,
sk_len *uint32,
pk []byte,
pk_len *uint32,
) yhsm2_enum.YH_rc
*/
const GOOD_CTX = "goctx123"
const TEST_MSG1 = "testing 123"
func main() {
wk := []byte{0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f}
sk := make([]byte, 200)
pk := make([]byte, 50)
var a, b uint32
var sk_len *uint32 = &a
var pk_len *uint32 = &b
yhsm2_k.YH_generate_ed25519_keypair(4481,
"XXXXXX",
"TestLabel",
"1",
wk,
sk,
sk_len,
pk,
pk_len)
fmt.Println(sk)
fmt.Println(*sk_len)
fmt.Println(pk)
fmt.Println(*pk_len)
c, err_cipher := aes.NewCipher(wk)
if err_cipher != nil {
panic(err_cipher)
}
ccm, err_init := aes_ccm.NewCCM(c, 16, 13)
if err_init != nil {
panic(err_init)
}
realpk, err_open := ccm.Open(nil, sk[0:13], sk[13:*sk_len], nil)
if err_open != nil {
panic(err_open)
} else {
fmt.Printf("Real PK:\n")
fmt.Println(len(realpk))
fmt.Println(realpk)
fmt.Println(string(realpk))
}
for i := 0; i<(len(sk)-64); i++ {
fmt.Printf("\n--- SignCreate (single) ---\n")
fmt.Printf("Create\n")
sig, createErr := hydro.SignCreate([]byte(TEST_MSG1), GOOD_CTX, sk[len(sk)-64-i:len(sk)-i])
if createErr != 0 {
panic("SignCreate returned non-zero")
}
// fmt.Printf("sig[%d]:\n%s\n", len(sig), hydro.Bin2hex(sig))
fmt.Printf("Verify\n")
sigVerified := hydro.SignVerify(sig, []byte(TEST_MSG1), GOOD_CTX, pk[0:32])
fmt.Print("sigVerified = ")
fmt.Println(sigVerified)
if sigVerified {
fmt.Println("------------------------------------------------------The index i searched is:")
fmt.Printf("%d\n", i)
}
}
}
go файл-обертки :
package yubihsm2_auth
/*
#cgo CFLAGS: -I/usr/include
#cgo LDFLAGS: -L/usr/lib -lyubihsm -lyubihsm_usb -lyubihsm_http
#include <stdlib.h>
#include <yubihsm.h>
yh_rc yh_generate_ed25519_keypair( uint16_t authkey,
const char* password,
const char* domains,
const char* label,
uint8_t* wrapkey,
uint8_t *sk_out,
size_t *sk_len,
uint8_t *pk_out,
size_t *pk_len);
*/
import "C"
import (
"unsafe"
//"fmt"
yhsm2_enum "github.com/Pymann/yubihsm2-enum"
)
/**
* Instantiate a new connector
*
* @param string password,
* @param string label,
* @param string domains,
* @param string capabilites,
* @param string del_capabilites
*
* @return #YHR_SUCCESS if successful.
* otherwise occured error.
*/
func YH_generate_ed25519_keypair( authkey uint16,
password,
label,
domains string,
wrapkey []byte,
sk []byte,
sk_len *uint32,
pk []byte,
pk_len *uint32) yhsm2_enum.YH_rc {
akey := C.uint16_t(authkey)
pass := C.CString(password)
lab := C.CString(label)
doms := C.CString(domains)
result := C.yh_generate_ed25519_keypair( akey,
pass,
lab,
doms,
(*C.uint8_t)(unsafe.Pointer(&wrapkey[0])),
(*C.uint8_t)(unsafe.Pointer(&sk[0])),
(*C.size_t)(unsafe.Pointer(sk_len)),
(*C.uint8_t)(unsafe.Pointer(&pk[0])),
(*C.size_t)(unsafe.Pointer(pk_len)) )
/*fmt.Println("In go")
fmt.Println(*sk_len)
fmt.Println(*pk_len)
fmt.Println(sk)
fmt.Println(pk)
fmt.Println("go ende")*/
C.free(unsafe.Pointer(pass))
C.free(unsafe.Pointer(lab))
C.free(unsafe.Pointer(doms))
return yhsm2_enum.YH_rc(result)
}
/*
typedef struct {
/// Object capabilities @see yh_capabilities
yh_capabilities capabilities; 8byte array
/// Object ID
uint16_t id; 2
/// Object length
uint16_t len; 2
/// Object domains
uint16_t domains; 2
/// Object type
yh_object_type type; 2|4
/// Object algorithm
yh_algorithm algorithm;
/// Object sequence
uint8_t sequence; 1
/// Object origin
uint8_t origin; 1
/// Object label. The label consists of raw bytes and is not restricted to printable characters or valid UTF-8 glyphs
char label[YH_OBJ_LABEL_LEN + 1]; 40+1
/// Object delegated capabilities
yh_capabilities delegated_capabilities; 8byte array
} yh_object_descriptor;
#pragma pack(pop)
*/
и c -код для этой функции:
#include <string.h>
#include <stdlib.h>
#include <yubihsm.h>
#include "internal.h"
#include <assert.h>
static void print_yhrc(yh_rc yrc);
yh_rc yh_generate_ed25519_keypair( uint16_t authkey,
const char* password,
const char* label,
const char* domains,
uint8_t* wrapkey,
uint8_t *sk_out,
size_t *sk_len,
uint8_t *pk_out,
size_t *pk_len) {
printf(password);printf("\n");
printf(label);printf("\n");
printf("%d",authkey);printf("\n");
static yh_connector connector_arg;
yh_connector *connector = &connector_arg;
yh_session *session = NULL;
yh_rc yrc = YHR_GENERIC_ERROR;
const char *connector_url = "yhusb://";
yrc = yh_init();
assert(yrc == YHR_SUCCESS);
yrc = yh_init_connector(connector_url, &connector);
assert(yrc == YHR_SUCCESS);
yrc = yh_connect(connector, 0);
if (yrc == YHR_SUCCESS) {
printf("Connected!\n");
//printf(yh_strerror(yrc));
} else {
printf("Connect error: %02d\n", (int)yrc);
}
yrc = yh_create_session_derived(connector, authkey, password,
sizeof(password), false, &session);
print_yhrc(yrc);
yrc = yh_authenticate_session(session);
print_yhrc(yrc);
uint8_t session_id;
yrc = yh_get_session_id(session, &session_id);
print_yhrc(yrc);
printf("Successfully established session %02d\n", session_id);
uint16_t domain_arg2;
uint16_t *domain = &domain_arg2;
printf("string_to_domains\n");
yrc = yh_string_to_domains(domains, domain);
print_yhrc(yrc);
yh_capabilities wrapkey_cap_arg;
yh_capabilities *wrapkey_cap = &wrapkey_cap_arg;
printf("WrapKey caps\n");
yrc = yh_string_to_capabilities("export-wrapped|import-wrapped|unwrap-data|wrap-data|exportable-under-wrap", wrapkey_cap);
print_yhrc(yrc);
uint16_t key_arg = 0; //let device generate id
uint16_t* key_p = &key_arg;
yh_capabilities yh_cap_arg;
yh_capabilities *yh_cap = &yh_cap_arg;
printf("Key caps\n");
yrc = yh_string_to_capabilities("sign-eddsa|exportable-under-wrap|unwrap-data|wrap-data", yh_cap);
print_yhrc(yrc);
uint16_t wrapkey_id = 0;
uint16_t* wrapkey_id_p = &wrapkey_id;
/*yh_rc yh_util_import_wrap_key(yh_session *session, uint16_t *key_id,
const char *label, uint16_t domains,
const yh_capabilities *capabilities,
yh_algorithm algorithm,
const yh_capabilities *delegated_capabilities,
const uint8_t *in, size_t in_len);*/
printf("Import WrapKey\n");
yrc = yh_util_import_wrap_key(session, wrapkey_id_p,
label, *domain,
wrapkey_cap,
YH_ALGO_AES128_CCM_WRAP,
yh_cap,
wrapkey, (size_t)16);
print_yhrc(yrc);
printf("GeneratedWrapKey ID: %02d\n", *wrapkey_id_p);
printf("Generate EDKey\n");
/*yh_rc yh_util_generate_ed_key(yh_session *session, uint16_t *key_id,
const char *label, uint16_t domains,
const yh_capabilities *capabilities,
yh_algorithm algorithm);*/
yrc = yh_util_generate_ed_key(session, key_p,
label,
*domain,
yh_cap,
YH_ALGO_EC_ED25519);
print_yhrc(yrc);
printf("GeneratedKey ID: %02d\n", *key_p);
size_t wrapout_len=10000;
size_t *wrapout_len_p=&wrapout_len;
uint8_t wrapoutcome[10000] = {};
/*yh_rc yh_util_export_wrapped(yh_session *session, uint16_t wrapping_key_id,
yh_object_type target_type, uint16_t target_id,
uint8_t *out, size_t *out_len);*/
printf("Export WrapData\n");
yrc = yh_util_export_wrapped(session,
*wrapkey_id_p,
YH_ASYMMETRIC_KEY,
*key_p,
wrapoutcome, wrapout_len_p);
sk_out = (uint8_t*)memcpy(sk_out,wrapoutcome,*wrapout_len_p);
*sk_len = *wrapout_len_p;
printf("Wrap-Out-Len %d\n", *wrapout_len_p);
print_yhrc(yrc);
printf("Get PublicKey\n");
uint8_t puboutcome[10000] = {};
size_t pubout_len_arg = 10000;
size_t* puboutcome_len = &pubout_len_arg;
yh_algorithm pubalgo;
yh_algorithm *pubalgo_p = &pubalgo;
yrc = yh_util_get_public_key(session, *key_p, puboutcome,
puboutcome_len, pubalgo_p);
pk_out = (uint8_t*)memcpy(pk_out,puboutcome,*puboutcome_len);
*pk_len = *puboutcome_len;
printf("Pub-Out-Len %d\n", *puboutcome_len);
printf("Algo: %02d\n", (uint8_t)pubalgo);
print_yhrc(yrc);
yrc = yh_util_close_session(session);
print_yhrc(yrc);
yrc = yh_disconnect(connector);
print_yhrc(yrc);
yrc = yh_exit();
return yrc;
}
static void print_yhrc(yh_rc yrc) {
if (yrc == YHR_SUCCESS) {
} else {
printf("Error: %02d\n", (int)yrc);
}
}
Итак, это вывод, метка экспортированных данных становится видимой, но я не могу найти действительная пара ключей:
TestLabel
4481
Connected!
Successfully established session 00
string_to_domains
WrapKey caps
Key caps
Import WrapKey
GeneratedWrapKey ID: 61130
Generate EDKey
GeneratedKey ID: 64207
Export WrapData
Wrap-Out-Len 184
Get PublicKey
Pub-Out-Len 32
Algo: 46
[165 107 3 89 88 0 0 0 0 0 0 0 58 149 161 94 145 198 236 25 25 28 76 188 93 49 126 31 56 188 152 65 148 160 50 102 196 228 172 10 8 87 194 108 134 90 3 171 182 149 155 98 165 108 173 253 58 96 141 101 69 57 91 150 31 249 108 82 73 77 148 130 111 255 232 80 26 171 214 140 226 130 196 188 58 142 191 236 177 79 29 138 116 15 146 173 254 152 97 174 174 93 111 104 211 163 81 191 238 129 243 218 137 81 213 33 212 1 243 23 230 57 50 171 190 50 101 105 226 194 182 33 244 4 5 11 234 9 161 56 133 229 228 131 100 148 47 61 90 144 137 5 165 31 14 120 76 176 25 113 96 124 220 243 111 162 154 147 53 72 143 50 76 160 243 194 203 7 23 104 246 52 12 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
184
[63 119 178 191 147 100 164 183 67 23 96 213 142 200 18 223 73 53 68 57 107 81 56 69 219 252 103 131 149 151 222 232 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
32
Real PK:
155
[29 0 0 0 96 0 1 1 0 250 207 0 96 0 1 3 46 0 1 84 101 115 116 76 97 98 101 108 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 73 116 177 146 67 29 163 181 168 205 177 53 14 74 125 82 117 238 234 205 6 28 64 204 212 163 15 216 0 135 198 96 66 158 211 201 57 244 170 204 245 2 53 94 27 73 75 121 114 93 31 190 88 110 116 146 193 178 188 234 128 77 150 3 63 119 178 191 147 100 164 183 67 23 96 213 142 200 18 223 73 53 68 57 107 81 56 69 219 252 103 131 149 151 222 232]
`��`.TestLabelIt��C���ͱ5J}Ru���@�ԣ���`B���9����5^IKyr]�Xnt������M�?w���d��C`Վ��I5D9kQ8E��g�����
--- SignCreate (single) ---
Create
Verify
sigVerified = false
--- SignCreate (single) ---
Create
Verify
sigVerified = false
Да, так много ложных подписей для каждого 64-байтового фрагмента экспортированного зашифрованного объекта.
Так что шоу должно go включено, поддержка ответила здесь: https://github.com/Yubico/yubihsm-shell/issues/74
Таким образом, экспортированные данные показывают sha512 га sh исходного семени, поэтому должно быть возможно взять:
func NewKeyFromSeed(seed []byte) PrivateKey {
/*if l := len(seed); l != SeedSize {
panic("ed25519: bad seed length: " + strconv.Itoa(l))
}
*/
digest := seed //sha512.Sum512(seed)
digest[0] &= 248
digest[31] &= 127
digest[31] |= 64
var A edwards25519.ExtendedGroupElement
var hBytes [32]byte
copy(hBytes[:], digest[:])
edwards25519.GeScalarMultBase(&A, &hBytes)
var publicKeyBytes [32]byte
A.ToBytes(&publicKeyBytes)
privateKey := make([]byte, PrivateKeySize)
copy(privateKey, seed)
copy(privateKey[32:], publicKeyBytes[:])
return privateKey
}
и сгенерировать действительную пару ключей, но это не так!
Больше редактировать: это функционал on также копирует в конце исходное начальное значение в закрытый ключ перед хэшированием.
Но YubiHSM2 экспортирует ha sh, поэтому он никогда не сможет восстановить privatKey, как определено стандартом RF C 8032?