@dfinity/agent
Version:
JavaScript and TypeScript library to interact with the Internet Computer
148 lines (133 loc) • 4.64 kB
text/typescript
import { Principal } from '@dfinity/principal';
import { type HttpAgentRequest } from './agent/http/types.ts';
import { requestIdOf } from './request_id.ts';
import { bytesToHex, concatBytes } from '@noble/hashes/utils';
import { IC_REQUEST_DOMAIN_SEPARATOR } from './constants.ts';
/**
* A Key Pair, containing a secret and public key.
*/
export interface KeyPair {
secretKey: Uint8Array;
publicKey: PublicKey;
}
/**
* A public key that is DER encoded. This is a branded Uint8Array.
*/
export type DerEncodedPublicKey = Uint8Array & { __derEncodedPublicKey__?: void };
/**
* A signature array buffer.
*/
export type Signature = Uint8Array & { __signature__: void };
/**
* A Public Key implementation.
*/
export interface PublicKey {
toDer(): DerEncodedPublicKey;
// rawKey, toRaw, and derKey are optional for backwards compatibility.
toRaw?(): Uint8Array;
rawKey?: Uint8Array;
derKey?: DerEncodedPublicKey;
}
/**
* A General Identity object. This does not have to be a private key (for example,
* the Anonymous identity), but it must be able to transform request.
*/
export interface Identity {
/**
* Get the principal represented by this identity. Normally should be a
* `Principal.selfAuthenticating()`.
*/
getPrincipal(): Principal;
/**
* Transform a request into a signed version of the request. This is done last
* after the transforms on the body of a request. The returned object can be
* anything, but must be serializable to CBOR.
*/
transformRequest(request: HttpAgentRequest): Promise<unknown>;
}
/**
* An Identity that can sign blobs.
*/
export abstract class SignIdentity implements Identity {
protected _principal: Principal | undefined;
/**
* Returns the public key that would match this identity's signature.
*/
public abstract getPublicKey(): PublicKey;
/**
* Signs a blob of data, with this identity's private key.
*/
public abstract sign(blob: Uint8Array): Promise<Signature>;
/**
* Get the principal represented by this identity. Normally should be a
* `Principal.selfAuthenticating()`.
*/
public getPrincipal(): Principal {
if (!this._principal) {
this._principal = Principal.selfAuthenticating(new Uint8Array(this.getPublicKey().toDer()));
}
return this._principal;
}
/**
* Transform a request into a signed version of the request. This is done last
* after the transforms on the body of a request. The returned object can be
* anything, but must be serializable to CBOR.
* @param request - internet computer request to transform
*/
public async transformRequest(request: HttpAgentRequest): Promise<unknown> {
const { body, ...fields } = request;
const requestId = requestIdOf(body);
return {
...fields,
body: {
content: body,
sender_pubkey: this.getPublicKey().toDer(),
sender_sig: await this.sign(concatBytes(IC_REQUEST_DOMAIN_SEPARATOR, requestId)),
},
};
}
}
export class AnonymousIdentity implements Identity {
public getPrincipal(): Principal {
return Principal.anonymous();
}
public async transformRequest(request: HttpAgentRequest): Promise<unknown> {
return {
...request,
body: { content: request.body },
};
}
}
/*
* We need to communicate with other agents on the page about identities,
* but those messages may need to go across boundaries where it's not possible to
* serialize/deserialize object prototypes easily.
* So these are lightweight, serializable objects that contain enough information to recreate
* SignIdentities, but don't commit to having all methods of SignIdentity.
*
* Use Case:
* * DOM Events that let differently-versioned components communicate to one another about
* Identities, even if they're using slightly different versions of agent packages to
* create/interpret them.
*/
export interface AnonymousIdentityDescriptor {
type: 'AnonymousIdentity';
}
export interface PublicKeyIdentityDescriptor {
type: 'PublicKeyIdentity';
publicKey: string;
}
export type IdentityDescriptor = AnonymousIdentityDescriptor | PublicKeyIdentityDescriptor;
/**
* Create an IdentityDescriptor from a @dfinity/identity Identity
* @param identity - identity describe in returned descriptor
*/
export function createIdentityDescriptor(
identity: SignIdentity | AnonymousIdentity,
): IdentityDescriptor {
const identityIndicator: IdentityDescriptor =
'getPublicKey' in identity
? { type: 'PublicKeyIdentity', publicKey: bytesToHex(identity.getPublicKey().toDer()) }
: { type: 'AnonymousIdentity' };
return identityIndicator;
}