UNPKG

@dfinity/vetkeys

Version:

JavaScript and TypeScript library to use Internet Computer vetKeys

478 lines (477 loc) 17.6 kB
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(); }