This documentation provides an overview of the implemented Nitrokey Webcrypt interface in Nitrokey 3. It includes high-level descriptions of the commands and low-level protocol details. Please note that this implementation is in the early stages and may undergo changes in the future.
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x0 | STATUS | None | {UNLOCKED,VERSION,SLOTS,PIN_ATTEMPTS} |
- | - |
0x1 | TEST_PING | Any / Raw | Any / Raw | - | - |
0x2 | TEST_CLEAR | None | None | - | - |
0x3 | TEST_REBOOT | None | None | - | - |
0x4 | LOGIN | {PIN} |
{TP} |
- | + |
0x5 | LOGOUT | None | None | - | - |
0x6 | FACTORY_RESET | None | None | - | + |
0x7 | RESERVED | - | - | - | + |
0x8 | SET_CONFIGURATION | {CONFIRMATION} |
None | - | + |
0x9 | GET_CONFIGURATION | None | {CONFIRMATION} |
- | + |
0x0A | SET_PIN | {PIN} |
None | - | + |
0x0B | CHANGE_PIN | {PIN,NEWPIN} |
None | - | + |
0x10 | INITIALIZE_SEED | {ENTROPY} |
{MASTER,SALT} |
+ | + |
0x11 | RESTORE_FROM_SEED | {MASTER,SALT} |
{HASH} |
+ | + |
0x12 | GENERATE_KEY | None | {PUBKEY,KEYHANDLE} |
+ | + |
0x13 | SIGN | {HASH,KEYHANDLE} |
{SIGNATURE,INHASH} |
+ | + |
0x14 | DECRYPT | {DATA,KEYHANDLE,[HMAC,ECCEKEY]} |
{DATA} |
+ | + |
0x15 | GENERATE_KEY_FROM_DATA | {HASH} |
{PUBKEY,KEYHANDLE} |
+ | + |
0x16 | GENERATE_RESIDENT_KEY | None | {PUBKEY,KEYHANDLE} |
+ | + |
0x17 | READ_RESIDENT_KEY_PUBLIC | {KEYHANDLE} |
{PUBKEY,KEYHANDLE} |
+ | + |
0x18 | DISCOVER_RESIDENT_KEYS | TBD | TBD | + | + |
0x19 | WRITE_RESIDENT_KEY | {RAW_KEY_DATA,[KEY_TYPE]} |
{PUBKEY,KEYHANDLE} |
+ | + |
Where for the given command:
- ID is a hexadecimal integer;
TEST_
prefixed commands are available only in the development firmware;- the TBD acronym means that the work for that command is planned
- plus
+
sign means a requirement for operation named by this specific column, whereas minus-
sign is the opposite; - column
Au
, short from authentication, marks authentication requirement with theLOGIN
command, before using this command; - column
Bt
, short from button, marks touch-button press requirement after the command is called, to proceed further.
Note that OpenPGP specific commands are missing from this description (to be updated).
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x10 | INITIALIZE_SEED | {ENTROPY} |
{MASTER,SALT} |
+ | + |
Sets random values (sourced from the HWRNG) to the Nitrokey Webcrypt's secrets - Master Key and Salt - and returns them to the caller for the backup purposes. The device produced random values are XOR'ed with the incoming ENTROPY field.
On the client application side these binary secrets should be translated to human readable word-based representation, Word Seed, similarly to BIP#39, e.g.:
witch collapse practice feed shame open despair creek road again ice least
In the future the secret will be returned in one field instead of two.
Note: Nitrokey Webcrypt's secrets should be guaranteed to always be initialized. It should not be possible to use them however unless confirmed that user has his backup Word Seed saved.
random_data[40] = HWRNG(40)
MASTER[32],SALT[8] = random_data[40] ^ ENTROPY[40]
Field | Size [B] | Description |
---|---|---|
ENTROPY |
40 | Client-sourced bytes to be mixed with HWRNG result |
Field | Size [B] | Description |
---|---|---|
MASTER |
32 | Nitrokey Webcrypt's Master Secret |
SALT |
8 | Salt |
ID | Mnemonic | Description |
---|---|---|
0xF5 | ERR_FAILED_LOADING_DATA | Error during preparing output |
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x11 | RESTORE_FROM_SEED | {MASTER,SALT} |
{HASH} |
+ | + |
Sets Nitrokey Webcrypt's secret values as received from the caller. For verification calculates SHA256 hash of the input and returns as HASH
.
HASH = SHA256(MASTER|SALT)
Field | Size [B] | Description |
---|---|---|
MASTER |
32 | Nitrokey Webcrypt's Master Secret |
SALT |
8 | Salt |
Field | Size [B] | Description |
---|---|---|
HASH |
32 | SHA256 hash of the sent MASTER+SALT |
ID | Mnemonic | Description |
---|---|---|
0xF3 | ERR_BAD_FORMAT | Incoming data are malformed |
0xF5 | ERR_FAILED_LOADING_DATA | Error during preparing output |
The wrapping operation is reused from the fido-authenticator crate:
- The private key is wrapped using a persistent wrapping key using ChaCha20-Poly1305 AEAD algorithm.
- The wrapped key is embedded into a KeyHandle data structure, containing additional metadata (RP ID, Usage Flags).
- The serialized KeyHandle structure is finally CBOR serialized and encrypted, resulting in a binary blob to be used with other commands.
key_private - private key structure
Encrypt = ChaCha20-Poly1305
Serialize = CBOR
key_private_enc = Encrypt(Serialize(key_private))
key_handle = Encrypt(Serialize(key_private_enc))
key_pub[64] = ECC_compute_public_key(key_private)
PUBKEY = key_pub
KEYHANDLE = key_handle
The deserialization method of the KeyHandle is reused from the fido-authenticator project.
- The encrypted KeyHandle is decrypted and deserialized to a KeyHandle structure using persistent encryption key.
- From the resulting KeyHandle structure the wrapped private key is decrypted and deserialized
- Finally, the wrapped private key is imported to the volatile in-memory keystore, and used for the further operations.
key_handle - serialized and encrypted KeyHandle structure
Decrypt = ChaCha20-Poly1305
Deserialize = CBOR
key_private_enc = Decrypt(Deserialize(key_handle))
key_private = Decrypt(Deserialize(key_private_enc))
The KeyHandles for Resident Keys are a serialized internal KeyID (16 B) identifier, along with some metadata fields reserved for the future use. This might change in the future.
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x12 | GENERATE_KEY | None | {PUBKEY,KEYHANDLE} |
+ | + |
0x15 | GENERATE_KEY_FROM_DATA | {HASH} |
{PUBKEY,KEYHANDLE} |
+ | + |
For the actual key generation the FIDO U2F / FIDO2 key generation and wrapping mechanism was reused. The passphrase is processed through a hash function (e.g. Argon2) with known parameters client side, and the hash result is sent to the device. The received hash is HMAC'ed with the Nitrokey Webcrypt's master key WC_MASTER_KEY
.
The hash function selection, use and parameters will be standardized in the future.
# Browser
hash[32] = Argon2(passphrase)
# Device
key_data_raw[32] = HMAC256(hash)
key_pub, key_priv = wc_new_keypair(key_data_raw, appid)
See the wrapping algorithm in the Key Handle description chapter.
To discuss:
- hash function selection and parameters;
- introducing a KDF-DO like object, containing parameters needed to calculate the hash from the passphrase client side.
Field | Size [B] | Description |
---|---|---|
HASH |
32 | Source data for key generation |
Field | Size [B] | Description |
---|---|---|
PUBKEY |
64 | Raw ECC public key |
KEYHANDLE |
250 | Key handle |
Both commands return the same errors listed below.
ID | Mnemonic | Description |
---|---|---|
0xF3 | ERR_BAD_FORMAT | Incoming data are malformed |
0xF5 | ERR_FAILED_LOADING_DATA | Error during key generation or preparing output |
0xFA | ERR_INTERNAL_ERROR | Unexpected condition encountered |
Random key generation follows the same path as from the hash, except that instead of the key_data_raw
a randomized 32 bytes value is used, sourced from the device's HWRNG. Resulting KEYHANDLE
can be stored off-device for the later use, e.g. locally in the browser (localStorage / cookie), or on a remote server.
See From hash (GENERATE_KEY_FROM_DATA) chapter for the full pseudocode, specifically key wrapping.
# Device
random_data[32] = HWRNG(32)
key_pub, key_priv = wc_new_keypair(random_data, appid)
None
Field | Size [B] | Description |
---|---|---|
PUBKEY |
64 | Raw ECC public key |
KEYHANDLE |
250 | Key handle |
ID | Mnemonic | Description |
---|---|---|
0xF5 | ERR_FAILED_LOADING_DATA | Error during key generation or preparing output |
0xFA | ERR_INTERNAL_ERROR | Unexpected condition encountered |
Work in progress.
To implement:
- Add cross-origin keys;
- Key attributes; To discuss:
- Replace HMAC with AES GCM or ChaCha20/ChaCha20-Poly1305;
- Introduce MAC-then-encrypt/MAC-then-pad-then-encrypt if needed.
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x13 | SIGN | {HASH,KEYHANDLE} |
{SIGNATURE,INHASH} |
+ | + |
Returns SIGNATURE
as a result, as well as the incoming hash HASH
.
KEYHANDLE
authenticity (whether it was generated with given Master Key and to use for given Origin) is verified before use.
Incoming HASH
data is repeated on the output for signature confirmation.
See the wrapping algorithm in the Key Handle description chapter.
The type of the SIGNATURE
signature depends on the used key algorithm, encoded in the keyhandle.
In pseudocode:
SIGNATURE = Sign(KEYHANDLE, hash)
INHASH = HASH
Using key encoded in KEYHANDLE
parameter command makes signature over the input hash HASH
using ECDSA.
The curve used by default is secp256r1
(NIST P-256 Random).
Signing operation for RSA keys uses PKCSv15 padding and SHA256 as the hash. The only supported size for the RSA keys is RSA 2048.
To implement:
- Support
secp256k1
curve (NIST P-256 Koblitz). - Support other algorithms.
Field | Size [B] | Description |
---|---|---|
HASH |
32 | Raw data to sign, typically SHA256 hash |
KEYHANDLE |
250 | Key handle |
Field | Size [B] | Description |
---|---|---|
SIGNATURE |
64 | ECC signature |
INHASH |
32 | Incoming raw data to sign |
ID | Mnemonic | Description |
---|---|---|
0xF3 | ERR_BAD_FORMAT | Incoming data are malformed |
0xF5 | ERR_FAILED_LOADING_DATA | Error during preparing output |
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x14 | DECRYPT | {DATA,KEYHANDLE,[HMAC,ECCEKEY]} |
{DATA} |
+ | + |
The type of the operation done on the DATA
ciphertext depends on the used key algorithm, encoded in the keyhandle.
KEYHANDLE
authenticity (whether it was generated with given Master Key and to use for given Origin) is verified before use.
See the wrapping algorithm in the Key Handle description chapter.
Decrypts data given in the DATA
field, using KEYHANDLE
Key Handle for regenerating the private key, and ECCEKEY
ephemeral ECC public key for deriving the shared secret using ECDH. Before that this command verifies the data by calculating HMAC over all the fields and comparing with incoming HMAC
field.
Requires PKCS#7 (RFC 5652) padded data to the length of multiple of 32.
Pseudocode:
shared_secret = ecc256_shared_secret(ECCEKEY)
data_len = len(DATA)
hmac_calc = HMAC256(shared_secret, DATA|ECCEKEY|data_len|KEYHANDLE)
if hmac_calc != HMAC: abort
plaintext = Decrypt(AES256, shared_secret, DATA)
Decrypts data given in the DATA
field, using KEYHANDLE
Key Handle for regenerating the private key.
KEYHANDLE
authenticity (whether it was generated with given Master Key and to use for given Origin) is verified before use.
The ciphertext should be encoded with PKCS#1v15 padding. HMAC
and ECCEKEY
should not be provided.
HMAC is not checked.
Pseudocode:
plaintext = Decrypt(RSA2048, KEYHANDLE, DATA)
DATA = plaintext
Field | Size [B] | Description |
---|---|---|
DATA |
32-128* | Data to decrypt |
KEYHANDLE |
250+ | Key handle |
HMAC |
32 | Calculated HMAC (ECC only) |
ECCEKEY |
64 | Raw ECC public key (ECC only) |
Field | Size [B] | Description |
---|---|---|
DATA |
32-128* | Decrypted data |
ID | Mnemonic | Description |
---|---|---|
0xF3 | ERR_BAD_FORMAT | Incoming data are malformed |
0xF5 | ERR_FAILED_LOADING_DATA | Error during preparing output |
*
- work in progress: - maximum data length will be increased.
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x0 | STATUS | None | {UNLOCKED,VERSION,SLOTS,PIN_ATTEMPTS} |
- | - |
Command requires authentication: no.
- should
SLOTS
number not be hidden to avoid fingerprinting.
None
Field | Size [B] | Description |
---|---|---|
VERSION |
1 | implemented Nitrokey Webcrypt's version |
SLOTS |
1 | number of left available Webcrypt's Resident Keys slots |
PIN_ATTEMPTS |
1 | PIN attempt counter's current value |
UNLOCKED |
1 | Return true if the session is open |
ID | Mnemonic | Description |
---|---|---|
0xF5 | ERR_FAILED_LOADING_DATA | Error during preparing output |
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x4 | LOGIN | {PIN} |
{TP} |
- | + |
This command allows to establish session by returning a session token upon presenting the correct PIN. If the PIN is invalid, the PIN attempt counter will be decreased. Once the latter reaches 0, the only further available operation will be FACTORY_RESET.
Field | Size [B] | Description |
---|---|---|
PIN |
4-64 | Current Webcrypt's PIN |
Field | Size [B] | Description |
---|---|---|
TP |
32 | Session token, a.k.a. temporary password |
ID | Mnemonic | Description |
---|---|---|
0xF1 | ERR_INVALID_PIN | The presented PIN is invalid |
0xF2 | ERR_NOT_ALLOWED | The PIN attempt counter is used up |
0xF5 | ERR_FAILED_LOADING_DATA | Error during preparing output |
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x5 | LOGOUT | None | None | - | - |
Clear all session related data, and remove all secrets from the memory.
None
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x6 | FACTORY_RESET | None | None | - | + |
Removes all the currently stored user data, and prepares the device for the new use.
Note: this command does not need PIN confirmation or session set.
None
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x8 | SET_CONFIGURATION | {CONFIRMATION} |
None | - | + |
0x9 | GET_CONFIGURATION | None | {CONFIRMATION} |
- | + |
This command allows to change the user settings in Webcrypt. Work in progress.
Field | Size [B] | Description |
---|---|---|
CONFIRMATION |
1 | Confirmation mode (WIP) |
ID | Mnemonic | Description |
---|---|---|
0xF5 | ERR_FAILED_LOADING_DATA | Error during preparing output |
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x0A | SET_PIN | {PIN} |
None | - | + |
0x0B | CHANGE_PIN | {PIN,NEWPIN} |
None | - | + |
The SET_PIN and CHANGE_PIN commands are for the PIN handling. The former allows to set the PIN, when there is none (e.g. just after factory reset operation), but afterwards it is not allowed to work. The further PIN changes require CHANGE_PIN command to be used. The PIN can be of length between 4 and 64 bytes.
Field | Size [B] | Description |
---|---|---|
PIN |
4-64 | SET_PIN: the current PIN to be set |
PIN |
4-64 | CHANGE_PIN: the current PIN |
NEWPIN |
4-64 | CHANGE_PIN: the new PIN |
None
ID | Mnemonic | Description |
---|---|---|
0xF5 | ERR_FAILED_LOADING_DATA | Error during preparing output |
0xF1 | INVALID_PIN | The provided PIN is invalid, or wrong length |
0xF2 | ERR_NOT_ALLOWED | SET_PIN: command use is not allowed, because the PIN is already set |
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x16 | GENERATE_RESIDENT_KEY | None | {PUBKEY,KEYHANDLE} |
+ | + |
0x17 | READ_RESIDENT_KEY_PUBLIC | {KEYHANDLE} |
{PUBKEY,KEYHANDLE} |
+ | + |
0x18 | DISCOVER_RESIDENT_KEYS | TBD | TBD | + | + |
0x19 | WRITE_RESIDENT_KEY | {RAW_KEY_DATA,[KEY_TYPE]} |
{PUBKEY,KEYHANDLE} |
+ | + |
Resident Keys (RK) are the keys stored on the device, allowing to be identified with a shorter keyhandle, or instead used as a storage means for the Relying Party, relieving it from the keeping of the secret material completely. Resident Keys can be generated, imported and used in the same way as their derived counterparts with the SIGN and DECRYPT commands.
Detailed description:
- GENERATE_RESIDENT_KEY - generates a RK on the device using local means, and returns a keyhandle to it (ECC only);
- READ_RESIDENT_KEY_PUBLIC - allows to read a public key of the given RK;
- DISCOVER_RESIDENT_KEYS - lists all the RKs available for the given Relying Party (work in progress);
- WRITE_RESIDENT_KEY - writes raw key data, as received from the RP, and returns a keyhandle to it.
Field | Size [B] | Description |
---|---|---|
KEYHANDLE |
250+ | The keyhandle bytes, allowing to either identify the Resident Key or the Derived Key |
RAW_KEY_DATA |
32+ | Raw key data, to be saved as a Resident Key. RSA raw keys have to be encoded in PKCS#8 DER. |
KEY_TYPE |
2 | Key type, encoded in int16: 0 = P256 , 1 = RSA 2K. Optional. Default: 0. |
Field | Size [B] | Description |
---|---|---|
PUBKEY |
65+ | The calculated public key for the given keyhandle. RSA public key is encoded in PKCS#8 DER. |
KEYHANDLE |
250+ | The keyhandle bytes, allowing to either identify the Resident Key or the Derived Key |
ID | Mnemonic | Description |
---|---|---|
0xF5 | ERR_FAILED_LOADING_DATA | Error during preparing output |
ID | Mnemonic | Parameters | Returns | Au | Bt |
---|---|---|---|---|---|
0x1 | TEST_PING | Any / Raw | Any / Raw | - | - |
0x2 | TEST_CLEAR | None | None | - | - |
0x3 | TEST_REBOOT | None | None | - | - |
These test commands are introduced to help in the development of the client applications, and are available only in the development version of the firmware:
TEST_PING
- send and receive data for transport tests (loopback).
Not implemented at the moment:
TEST_CLEAR
- clear the current Nitrokey Webcrypt's state;TEST_REBOOT
- reboot device.
Following errors are common to all commands requiring authorization.
ID | Mnemonic | Description |
---|---|---|
0xF0 | ERR_REQ_AUTH | Command needs to be authorized by PIN * |
0xF1 | ERR_INVALID_PIN | Provided PIN is invalid |
0xF2 | ERR_NOT_ALLOWED | The given key's origin does not match the one of the request |
0xF3 | ERR_BAD_FORMAT | The given key's origin does not match the one of the request |
0xF4 | ERR_USER_NOT_PRESENT | User has not pressed touch button in time |
0xF5 | ERR_FAILED_LOADING_DATA | There was an error while preparing the result of the execution |
0xFD | ERR_BAD_ORIGIN | The given key's origin does not match the one of the request |
Notes:
- (*)
ERR_REQ_AUTH
should be returned, when: for FIDO U2F the session token was not provided in the data, for FIDO2 the PIN was not requested from the user (userVerification: "discouraged"
)
Communication is based on the Webauthn / FIDO2 API, which by itself allows to communicate with FIDO Security Keys in FIDO2 enabled browsers on all platforms, as well as through NFC and Bluetooth. Such feature is here reused as a an universal communication tunnel to the Nitrokey Webcrypt enabled device, making it plug-and-play and working out of the box with many configurations.
This chapter is still a work in progress.
Nitrokey Webcrypt's communication is based on the Request-Response message exchange pattern, where communication is initiated always by the host, and each data update requires sending the request.
Each Nitrokey Webcrypt's request is sent over Webauthn using MakeAssertion operation. It allows to transfer 255 bytes to the device, and receive 73 bytes back. The data fields used are:
key handle
for sending to device;signature
for receiving from device.
For low-level communication two commands are required:
WRITE
- to write to the Nitrokey Webcrypt's incoming buffer on the device;READ
- to read from the Nitrokey Webcrypt's outgoing buffer on the device;
Last packet of the WRITE
protocol operation executes the command. If there are any results, these will be available in the outgoing buffer, from which client can download the content using READ
commands.
in the future this might be minimalized by removing the redundant first call to READ
by moving first part of the results to the WRITE
operation response (similarly to CTAP).
Offset | Length | Mnemonic | Comments |
---|---|---|---|
0 | 1 | WEBCRYPT_CONST | Always equal to 0x22 . |
1 | 4 | __HEADER | Nitrokey Webcrypt's magic value to recognize extension over FIDO2 |
5 | 1 | COMM_ID | Operation: WRITE (0x01 ) or READ (0x02 ) |
6 | 1 | PACKET_NUM | This packet number, 0-255 |
7 | 1 | PACKET_CNT | Total packet count, 0-255 |
8 | 1 | CHUNK_SIZE | Size of the data chunk, 0-255 |
9 | 1 | CHUNK_LEN | Length of the given data chunk, 0-CHUNK_SIZE |
10 | CHUNK_LEN | DATA | Data to send |
Notes:
- Having dynamic
CHUNK_SIZE
allows to change the communication parameters on the fly, and depending on the platform conditions. - Introducing redundant information in the form of the packet number and count allows identifying potential transmission issues, like doubled packets (Windows 10 Webauthn handling issue).
- Magic value is:
__HEADER = 0x8C2790F6
. - In the future packet format might be modified to be more compact by removing redundant information (e.g. removing packet sequence information and the current chunk length, but leaving the chunk size; similarly to CTAP).
Offset | Length | Mnemonic | Comments |
---|---|---|---|
0 | 1 | COMMAND_ID | Command ID to execute |
1 | CHUNK_LEN-1 | DATA | CBOR encoded arguments to the command |
Offset | Length | Mnemonic | Comments |
---|---|---|---|
0 | 1 | RESULT | Result code |
Execution's result code are described under each command description.
Offset | Length | Mnemonic | Comments |
---|---|---|---|
0 | 2 | DATA_LEN | Data length N |
2 | 1 | CMD_ID | Command ID that produced result |
3 | DATA_LEN | DATA | Data received |
All parameters to the commands sent in the DATA
field of the data packet are CBOR(Concise Binary Object Representation, RFC7049) encoded key-value maps. This method was chosen due to following:
- FIDO2 requires CBOR encoded parameters as well, hence parser and encoder are provided already for FIDO2 supporting devices.
- CBOR handling libraries are available for all major languages, including JavaScript, where the client applications are meant to be developed.
Following is an example Nitrokey Webcrypt packet with WRITE
operation for the STATUS
command.
This packet should be provided as an argument for the Webauthn MakeAssertion's allowCredentials::id
parameter.
The following are connections between the FIDO2 and Nitrokey Webcrypt:
- On FIDO2 factory reset the Nitrokey Webcrypt's secrets should be reinitialized to random values.
- The PIN is shared between the FIDO2 and Nitrokey Webcrypt.
- The secrets are separated and never cross-used between FIDO2 and Nitrokey Webcrypt.
- The FIDO2 PIN attempt counter should decrease on failed login over Nitrokey Webcrypt.
- The FIDO2 use counter should not change during the use of Nitrokey Webcrypt.
Below is an example of Javascript API usage with OpenPGP.js.
class WebCryptHardwareKeysPlugin {
async serialNumber() {
return new Uint8Array(16).fill('A'.charCodeAt(0));
}
date() {
return this.webcrypt_date ? new Date(this.webcrypt_date) : new Date(2019, 1, 1);
} // the default WebCrypt date for the created keys
async init() {
if (this.public_sign === undefined) {
await WEBCRYPT_LOGIN(WEBCRYPT_DEFAULT_PIN, statusCallback);
const res = await WEBCRYPT_OPENPGP_INFO(statusCallback);
this.public_encr = res.encr_pubkey;
this.public_sign = res.sign_pubkey;
this.webcrypt_date = res.date;
}
}
async agree({ curve, V, Q, d }) {
console.log({ curve, V, Q, d });
const agreed_secret = await WEBCRYPT_OPENPGP_DECRYPT(statusCallback, V);
return { secretKey: d, sharedKey: agreed_secret };
}
async sign({ oid, hashAlgo, data, Q, d, hashed }) {
const res = await WEBCRYPT_OPENPGP_SIGN(statusCallback, data);
const resb = hexStringToByte(res);
const r = resb.slice(0, 32);
const s = resb.slice(32, 64);
const reso = { r, s };
return reso;
}
async generate({ algorithmName, curveName, rsaBits }) {
let selected_pk = this.public_sign;
if (algorithmName === openpgp.enums.publicKey.ecdh) {
selected_pk = this.public_encr;
console.warn(`Selecting subkey: ${selected_pk} for encryption`);
} else if (algorithmName === openpgp.enums.publicKey.ecdsa) {
console.warn(`Selecting main: ${selected_pk} for signing`);
} else {
console.error(`Not supported algorithm: ${algorithmName}`);
throw new Error(`Not supported algorithm: ${algorithmName}`);
}
return { publicKey: selected_pk, privateKey: null };
}
}
const plugin = new WebCryptHardwareKeysPlugin();
WebcryptConnection(statusCallback){
await Webcrypt_Logout(statusCallback);
await Webcrypt_FactoryReset(statusCallback);
await Webcrypt_Status(statusCallback);
await Webcrypt_SetPin(statusCallback, new CommandSetPinParams(new_pin));
await Webcrypt_Login(statusCallback, new CommandLoginParams(new_pin));
await plugin.init();
const { privateKey: webcrypt_privateKey, publicKey: webcrypt_publicKey } = await openpgp.generateKey({
curve: 'p256',
userIDs: [{ name: 'Jon Smith', email: '[email protected]' }],
format: 'object',
date: plugin.date(),
config: { hardwareKeys: plugin }
});
}