@@ -85,6 +85,69 @@ function prefixWithLen(input: Uint8Array): Uint8Array {
8585 return result ;
8686}
8787
88+ /**
89+ * VetKD master key
90+ *
91+ * The VetKD subnet contains a small number of master keys, from which canister
92+ * keys are derived. In turn, many keys can be derived from the canister keys
93+ * using a context string.
94+ */
95+ export class MasterPublicKey {
96+ readonly #pk: G2Point ;
97+
98+ /**
99+ * Read a MasterPublicKey from the bytestring encoding
100+ *
101+ * Normally the bytes provided here will have been returned by
102+ * the `vetkd_public_key` management canister interface.
103+ */
104+ static deserialize ( bytes : Uint8Array ) : MasterPublicKey {
105+ return new MasterPublicKey ( bls12_381 . G2 . ProjectivePoint . fromHex ( bytes ) ) ;
106+ }
107+
108+ /**
109+ * Derive a canister master key from the subnet master key
110+ *
111+ * To create the derived public key in VetKD, a two step derivation is performed. The first step
112+ * creates a key that is specific to the canister that is making VetKD requests to the
113+ * management canister, sometimes called canister master key.
114+ *
115+ * This function can be used to compute canister master keys knowing just the subnet master key
116+ * plus the canister identity. This avoids having to interact with the IC for performing this
117+ * computation.
118+ */
119+ deriveKey ( canister_id : Uint8Array ) : DerivedPublicKey {
120+ const dst = "ic-vetkd-bls12-381-g2-canister-id" ;
121+ const pkbytes = this . publicKeyBytes ( ) ;
122+ const ro_input = new Uint8Array ( [
123+ ...prefixWithLen ( pkbytes ) ,
124+ ...prefixWithLen ( canister_id ) ,
125+ ] ) ;
126+ const offset = hashToScalar ( ro_input , dst ) ;
127+ const g2_offset = bls12_381 . G2 . ProjectivePoint . BASE . multiply ( offset ) ;
128+ return new DerivedPublicKey ( this . #pk. add ( g2_offset ) ) ;
129+ }
130+
131+ /**
132+ * Return the bytestring encoding of the master public key
133+ */
134+ publicKeyBytes ( ) : Uint8Array {
135+ return this . #pk. toRawBytes ( true ) ;
136+ }
137+
138+ /**
139+ * TODO CRP-2797 add getter for the production subnet key once this has been
140+ * generated.
141+ */
142+
143+ /**
144+ * @internal constructor
145+ */
146+ constructor ( pk : G2Point ) {
147+ this . #pk = pk ;
148+ }
149+ }
150+
88151/**
89152 * VetKD derived public key
90153 *
@@ -127,7 +190,12 @@ export class DerivedPublicKey {
127190 return this ;
128191 } else {
129192 const dst = "ic-vetkd-bls12-381-g2-context" ;
130- const offset = hashToScalar ( prefixWithLen ( context ) , dst ) ;
193+ const pkbytes = this . publicKeyBytes ( ) ;
194+ const ro_input = new Uint8Array ( [
195+ ...prefixWithLen ( pkbytes ) ,
196+ ...prefixWithLen ( context ) ,
197+ ] ) ;
198+ const offset = hashToScalar ( ro_input , dst ) ;
131199 const g2_offset =
132200 bls12_381 . G2 . ProjectivePoint . BASE . multiply ( offset ) ;
133201 return new DerivedPublicKey ( this . getPoint ( ) . add ( g2_offset ) ) ;
0 commit comments