@dfinity/vetkeys
Version:
JavaScript and TypeScript library to use Internet Computer vetKeys
478 lines (477 loc) • 17.6 kB
TypeScript
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
import { Fp, Fp2 } from '@noble/curves/abstract/tower';
import { Principal } from '@dfinity/principal';
export type G1Point = ProjPointType<Fp>;
export type G2Point = ProjPointType<Fp2>;
/**
* Transport Secret Key
*
* Applications using VetKD create an ephemeral transport secret key and send
* the public key to the IC as part of their VetKD request. The returned VetKey
* is encrypted, and can only be decrypted using the transport secret key.
*/
export declare class TransportSecretKey {
#private;
/**
* Create a random transport secret key
*/
static random(): TransportSecretKey;
/**
* Deserialize TransportSecretKey from a bytestring
*
* The passed value would typically be a string previously returned
* by calling serialize on a randomly-created TransportSecretKey.
*/
static deserialize(sk: Uint8Array): TransportSecretKey;
/**
* Return the encoding of the transport public key; this value is
* sent to the IC
*/
publicKeyBytes(): Uint8Array;
/**
* Return the transport secret key value
*
* Applications would not normally need to call this
*/
serialize(): Uint8Array;
/**
* @internal constructor
*/
private constructor();
}
/**
* Check if a transport public key is valid
*
* This tests if the passed byte array is of the expected size and encodes
* a valid group element.
*/
export declare function isValidTransportPublicKey(tpk: Uint8Array): boolean;
/**
* Enumeration identifying possible master public keys
*/
export declare enum MasterPublicKeyId {
/** The production key generated in June 2025 */
KEY_1 = "key_1",
/** The test key generated in May 2025 */
TEST_KEY_1 = "test_key_1"
}
/**
* VetKD master key
*
* The VetKD subnet contains a small number of master keys, from which canister
* keys are derived. In turn, many keys can be derived from the canister keys
* using a context string.
*/
export declare class MasterPublicKey {
#private;
/**
* Read a MasterPublicKey from the bytestring encoding
*
* Normally the bytes provided here will have been returned by
* the `vetkd_public_key` management canister interface.
*/
static deserialize(bytes: Uint8Array): MasterPublicKey;
/**
* Derive a canister master key from the subnet master key
*
* To create the derived public key in VetKD, a two step derivation is performed. The first step
* creates a key that is specific to the canister that is making VetKD requests to the
* management canister, sometimes called canister master key.
*
* This function can be used to compute canister master keys knowing just the subnet master key
* plus the canister identity. This avoids having to interact with the IC for performing this
* computation.
*/
deriveCanisterKey(canisterId: Uint8Array): DerivedPublicKey;
/**
* Return the bytestring encoding of the master public key
*/
publicKeyBytes(): Uint8Array;
/**
* Return the hardcoded master public key used on IC
*
* This allows performing public key derivation offline
*/
static productionKey(keyId?: MasterPublicKeyId): MasterPublicKey;
/**
* @internal constructor
*/
private constructor();
}
/**
* VetKD derived public key
*
* An unencrypted VetKey is a BLS signature generated with a canister-specific
* key. This type represents such keys.
*/
export declare class DerivedPublicKey {
#private;
/**
* Read a DerivedPublicKey from the bytestring encoding
*
* Normally the bytes provided here will have been returned by
* the `vetkd_public_key` management canister interface.
*/
static deserialize(bytes: Uint8Array): DerivedPublicKey;
/**
* Perform second-stage derivation of a public key
*
* To create the derived public key in VetKD, a two step derivation is performed. The first step
* creates a key that is specific to the canister that is making VetKD requests to the
* management canister, sometimes called canister master key. The second step incorporates the
* "derivation context" value provided to the `vetkd_public_key` management canister interface.
*
* If `vetkd_public_key` is invoked with an empty derivation context, it simply returns the
* canister master key. Then the second derivation step can be done offline, using this
* function. This is useful if you wish to derive multiple keys without having to interact with
* the IC each time.
*
* If `context` is empty, then this simply returns the underlying key. This matches the behavior
* of `vetkd_public_key`
*/
deriveSubKey(context: Uint8Array): DerivedPublicKey;
/**
* Return the bytestring encoding of the derived public key
*
* Applications would not normally need to call this, unless they
* are using VetKD for creating a random beacon, in which case
* these bytes are used by anyone verifying the beacon.
*/
publicKeyBytes(): Uint8Array;
/**
* @internal getter returning the point element of the derived public key
*
* Applications would not normally need to call this
*/
getPoint(): G2Point;
/**
* @internal constructor
*
* This is public for typing reasons but there should be no need
* for an application to call this.
*/
constructor(pk: G2Point);
}
/**
* Hash an input to a scalar in the BLS12-381 group
*
* This is useful if you want to derive a BLS12-381 secret key from some other
* input data, but this is not a common operation.
*/
export declare function hashToScalar(input: Uint8Array, domainSep: string): bigint;
/**
* @internal derive a symmetric key from the provided input
*
* The `input` parameter should be a sufficiently long random input generated
* in a secure way. 256 bits (32 bytes) or longer is preferable.
*
* The `domainSep` parameter should be a string unique to your application and
* also your usage of the resulting key. For example say your application
* "my-app" is deriving two keys, one for usage "foo" and the other for
* "bar". You might use as domain separators "my-app-foo" and "my-app-bar".
*
* The returned Uint8Array will be `outputLength` bytes long.
*/
export declare function deriveSymmetricKey(input: Uint8Array, domainSep: Uint8Array | string, outputLength: number): Uint8Array;
/**
* @internal hash a derived public key plus a message into the BLS12-381 G1 group
*
* This is not normally needed by applications using VetKD.
*/
export declare function augmentedHashToG1(pk: DerivedPublicKey, message: Uint8Array): G1Point;
/**
* Verify a BLS signature
*
* A VetKey is in the end a valid BLS signature; this function checks that a
* provided BLS signature is the valid one for the provided public key and
* message.
*
* Specifically this verifies "augmented" BLS signature, which includes the
* public key of the signer as an input to the hash. This addition ensures that
* messages signed by different public keys are distinct.
*
* See section 3.2 of the IETF draft `draft-irtf-cfrg-bls-signature` for details.
*
* When a VetKey struct is created (using EncryptedVetKey.decryptAndVerify) the signature
* is already verified, so using this function is only necessary when
* using a vetKey as a VRF or for threshold BLS signatures, with the bytes obtained
* from VetKey.signatureBytes.
*/
export declare function verifyBlsSignature(pk: DerivedPublicKey, message: Uint8Array, signature: G1Point | Uint8Array): boolean;
/**
* A VetKey (verifiably encrypted threshold key)
*
* This is the end product of executing the VetKD protocol.
*
* Internally a VetKey is a valid BLS signature for the bytestring
* `input` which provided when calling the `vetkd_derive_encrypted_key`
* management canister interface.
*
* For certain usages, such as a beacon, the VetKey is actually used directly.
* However the more common usage of VetKD protocol is for distribution of
* encryption keys (eg AES keys to encrypt content).
*/
export declare class VetKey {
#private;
/**
* Return the VetKey bytes, aka the BLS signature
*
* Use the raw bytes only if your design makes use of the fact that VetKeys
* are BLS signatures (eg for random beacon or threshold BLS signature
* generation). If you are using VetKD for key distribution, instead use
* deriveSymmetricKey or asHkdfCryptoKey
*/
signatureBytes(): Uint8Array;
/**
* Return the serialization of the VetKey
*
* This is the byte encoding of the unencrypted VetKey.
*/
serialize(): Uint8Array;
/**
* Derive a symmetric key of the requested length from the VetKey
*
* As an alternative to this function consider using asDerivedKeyMaterial,
* which uses the WebCrypto API and prevents export of the underlying key.
*
* The `domainSep` parameter should be a string unique to your application and
* also your usage of the resulting key. For example say your application
* "my-app" is deriving two keys, one for usage "foo" and the other for
* "bar". You might use as domain separators "my-app-foo" and "my-app-bar".
*
* The returned Uint8Array will be `outputLength` bytes long.
*/
deriveSymmetricKey(domainSep: Uint8Array | string, outputLength: number): Uint8Array;
/**
* Return a DerivedKeyMaterial type which is suitable for further key derivation
*/
asDerivedKeyMaterial(): Promise<DerivedKeyMaterial>;
/**
* Deserialize a VetKey from the 48 byte encoding of the BLS signature
*
* This deserializes the same value as returned by serialize (or signatureBytes)
*/
static deserialize(bytes: Uint8Array): VetKey;
/**
* @internal getter returning the point object of the VetKey
*
* Applications would not usually need to call this
*/
getPoint(): G1Point;
/**
* @internal constructor
*
* This is public for typing reasons but there is no reason for an application
* to call this constructor.
*/
constructor(pt: G1Point);
}
export declare class DerivedKeyMaterial {
#private;
/**
* @internal constructor
*/
private constructor();
static fromCryptoKey(cryptokey: CryptoKey): DerivedKeyMaterial;
/**
* @internal constructor
*/
static setup(bytes: Uint8Array): Promise<DerivedKeyMaterial>;
/**
* Return the CryptoKey
*/
getCryptoKey(): CryptoKey;
/**
* Return a WebCrypto CryptoKey handle suitable for AES-GCM encryption/decryption
*
* The key is derived using HKDF with the provided domain separator
*
* The CryptoKey is not exportable
*/
deriveAesGcmCryptoKey(domainSep: Uint8Array | string): Promise<CryptoKey>;
/**
* Encrypt the provided message using AES-GCM and a key derived using HKDF
*
* The GCM key is derived using HKDF with the provided domain separator
*/
encryptMessage(message: Uint8Array | string, domainSep: Uint8Array | string): Promise<Uint8Array>;
/**
* Decrypt the provided ciphertext using AES-GCM and a key derived using HKDF
*
* The GCM key is derived using HKDF with the provided domain separator
*/
decryptMessage(message: Uint8Array, domainSep: Uint8Array | string): Promise<Uint8Array>;
}
export declare class EncryptedVetKey {
#private;
/**
* Parse an encrypted key returned by the `vetkd_derive_encrypted_key`
* managment canister interface
*/
static deserialize(bytes: Uint8Array): EncryptedVetKey;
/**
* Decrypt the encrypted key returning a VetKey
*/
decryptAndVerify(tsk: TransportSecretKey, dpk: DerivedPublicKey, input: Uint8Array): VetKey;
/**
* @internal constructor
*/
private constructor();
}
/**
* An identity used for identity based encryption
*
* As far as the IBE encryption scheme goes this is simply an opauqe bytestring
* We provide a type to make code using the IBE a bit easier to understand
*/
export declare class IbeIdentity {
#private;
private constructor();
/**
* Create an identity from a byte string
*/
static fromBytes(bytes: Uint8Array): IbeIdentity;
/**
* Create an identity from a string
*/
static fromString(bytes: string): IbeIdentity;
/**
* Create an identity from a Principal
*/
static fromPrincipal(principal: Principal): IbeIdentity;
/**
* @internal getter returning the encoded
*/
getBytes(): Uint8Array;
}
/**
* A random seed, used for identity based encryption
*/
export declare class IbeSeed {
#private;
private constructor();
/**
* Create a seed for IBE encryption from a byte string
*
* This input should be randomly chosen by a secure random number generator.
* If the seed is not securely generated the IBE scheme will be insecure.
*
* At least 128 bits (16 bytes) must be provided.
*
* If the input is exactly 256 bits it is used directly. Otherwise the input
* is hashed with HKDF to produce a 256 bit seed.
*/
static fromBytes(bytes: Uint8Array): IbeSeed;
/**
* Create a random seed for IBE encryption
*/
static random(): IbeSeed;
/**
* @internal getter returning the seed bytes
*/
getBytes(): Uint8Array;
}
/**
* IBE (Identity Based Encryption)
*/
export declare class IbeCiphertext {
#private;
/**
* Helper function for determining the size of an IBE ciphertext in bytes.
*/
static ciphertextSize(plaintextSize: number): number;
/**
* Helper function for determining the size of an IBE plaintext in bytes.
*/
static plaintextSize(ciphertextSize: number): number;
/**
* Serialize the IBE ciphertext to a bytestring
*/
serialize(): Uint8Array;
/**
* Deserialize an IBE ciphertext
*/
static deserialize(bytes: Uint8Array): IbeCiphertext;
/**
* Encrypt a message using IBE, returning the ciphertext
*
* Any user who is able to retrieve the VetKey for the specified derived public key and
* identity will be able to decrypt this message.
*
* There is no fixed upper bound on the size of the message that can be encrypted using
* this scheme. However, internally during the encryption process several heap allocations
* are performed which are approximately the same length as the message itself, so
* encrypting or decrypting very large messages may result in memory allocation errors.
*
* If you anticipate using IBE to encrypt very large messages, consider using IBE just to
* encrypt a symmetric key, and then using a standard cipher such as AES-GCM to encrypt the
* data.
*
* The seed parameter must be a randomly generated value that was generated just for this
* one message. Using it for a second message, or for any other purpose, compromises the
* security of the IBE scheme.
*/
static encrypt(dpk: DerivedPublicKey, identity: IbeIdentity, msg: Uint8Array, seed: IbeSeed): IbeCiphertext;
/**
* Decrypt an IBE ciphertext, returning the message
*
* There is no fixed upper bound on the size of the message that can be encrypted using
* this scheme. However, internally during the encryption process several heap allocations
* are performed which are approximately the same length as the message itself, so
* encrypting or decrypting very large messages may result in memory allocation errors.
*/
decrypt(vetkd: VetKey): Uint8Array;
/**
* Private constructor
*/
private constructor();
}
/**
* VRF (Verifiable Random Function) Output
*
* VetKD can be used to construct a VRF, which is a public key version of a
* keyed hash. Like a standard keyed hash, it takes an input string and produces
* a output string which is indistinguishable from random. The difference
* between a VRF and a normal keyed hash is that a VRF can only be computed
* by someone with access to the VRF secret key, while the VRF output can be verified
* by any party with access to the public key.
*
* For some general background on VRFs consult [RFC 9381](https://www.rfc-editor.org/rfc/rfc9381.html)
*/
export declare class VrfOutput {
#private;
private static computeVrfHash;
/**
* Serialize a VrfOutput to a byte string
*/
serialize(): Uint8Array;
/**
* Deserialize and verify a VrfOutput
*
* Note this verifies the VrfOutput with respect to the derived public key
* and VRF input which are included in the struct. It is the responsibility
* of the application to examine the return value of `publicKey` and `input`
* and ensure these values make sense in the context where this VRF is being
* used.
*/
static deserialize(bytes: Uint8Array): VrfOutput;
/**
* Return the public key under which this VRF output was derived
*/
publicKey(): DerivedPublicKey;
/**
* Return the input that was used to create this VRF output
*/
input(): Uint8Array;
/**
* Return the VRF output
*
* This is a random-looking value which was provably generated by some party with
* access to the VRF secret key.
*/
output(): Uint8Array;
/**
* Private constructor
*/
private constructor();
}