@@ -9,11 +9,19 @@ import { keyAgreement, pemPublicToCrypto } from '../src/nanotdf-crypto/index.js'
99import { generateRandomNumber } from '../src/nanotdf-crypto/generateRandomNumber.js' ;
1010import { removePemFormatting } from '../tdf3/src/crypto/crypto-utils.js' ;
1111import { Binary } from '../tdf3/index.js' ;
12- import { type KeyAccessObject } from '../tdf3/src/models/index.js' ;
1312import { valueFor } from './web/policy/mock-attrs.js' ;
1413import { AttributeAndValue } from '../src/policy/attributes.js' ;
1514import { ztdfSalt } from '../tdf3/src/crypto/salt.js' ;
1615
16+ import { create , toJsonString , fromJson } from '@bufbuild/protobuf' ;
17+ import { ValueSchema } from "@bufbuild/protobuf/wkt" ;
18+ import {
19+ PolicyRewrapResultSchema ,
20+ KeyAccessRewrapResultSchema ,
21+ RewrapResponseSchema ,
22+ UnsignedRewrapRequestSchema
23+ } from '../src/platform/kas/kas_pb.js' ;
24+
1725const Mocks = getMocks ( ) ;
1826
1927function range ( start : number , end : number ) : Uint8Array {
@@ -24,18 +32,6 @@ function range(start: number, end: number): Uint8Array {
2432 return new Uint8Array ( result ) ;
2533}
2634
27- type RewrapBody = {
28- algorithm : 'RS256' | 'ec:secp256r1' ;
29- keyAccess : KeyAccessObject & {
30- header ?: string ;
31- } ;
32- policy : string ;
33- clientPublicKey : string ;
34- // testing only
35- invalidKey : string ;
36- invalidField : string ;
37- } ;
38-
3935function concat ( b : ArrayBufferView [ ] ) {
4036 const length = b . reduce ( ( lk , ak ) => lk + ak . byteLength , 0 ) ;
4137 const buf = new Uint8Array ( length ) ;
@@ -164,33 +160,34 @@ const kas: RequestListener = async (req, res) => {
164160 return ;
165161 }
166162
167- const rewrap = JSON . parse ( requestBody as string ) as RewrapBody ;
163+ const rewrap = fromJson ( UnsignedRewrapRequestSchema , JSON . parse ( requestBody as string ) ) ;
168164 console . log ( '[INFO]: rewrap request body: ' , rewrap ) ;
169165 const clientPublicKey = await pemPublicToCrypto ( rewrap . clientPublicKey ) ;
170166 if ( ! clientPublicKey || clientPublicKey . type !== 'public' ) {
171167 res . writeHead ( 400 ) ;
172168 res . end ( '{"error": "Invalid client public key"}' ) ;
173169 return ;
174170 }
175- const isZTDF = ! rewrap . keyAccess . header ;
171+ const kaoheader = rewrap . requests ?. [ 0 ] ?. keyAccessObjects ?. [ 0 ] ?. keyAccessObject ?. header ;
172+ const isZTDF = ! kaoheader || kaoheader . length === 0 ;
176173 if ( isZTDF ) {
177- if ( ! rewrap . keyAccess . wrappedKey ) {
174+ const wk = rewrap . requests ?. [ 0 ] ?. keyAccessObjects ?. [ 0 ] ?. keyAccessObject ?. wrappedKey ;
175+ if ( ! wk || wk . length === 0 ) {
178176 res . writeHead ( 400 ) ;
179177 res . end ( '{"error": "Invalid wrapped key"}' ) ;
180178 return ;
181179 }
182- const wk = base64 . decodeArrayBuffer ( rewrap . keyAccess . wrappedKey ) ;
183- const isECWrapped = rewrap . keyAccess . kid == 'e1' ;
180+ const isECWrapped = rewrap . requests ?. [ 0 ] ?. keyAccessObjects ?. [ 0 ] ?. keyAccessObject ?. kid == 'e1' ;
184181 // Decrypt the wrapped key from TDF3
185182 let dek : Binary ;
186183 if ( isECWrapped ) {
187- if ( ! rewrap . keyAccess . ephemeralPublicKey ) {
184+ if ( ! rewrap . requests ?. [ 0 ] ?. keyAccessObjects ?. [ 0 ] ?. keyAccessObject ? .ephemeralPublicKey ) {
188185 res . writeHead ( 400 ) ;
189186 res . end ( '{"error": "Nil ephemeral public key"}' ) ;
190187 return ;
191188 }
192189 const ephemeralKey : CryptoKey = await pemPublicToCrypto (
193- rewrap . keyAccess . ephemeralPublicKey
190+ rewrap . requests ?. [ 0 ] ?. keyAccessObjects ?. [ 0 ] ?. keyAccessObject ? .ephemeralPublicKey
194191 ) ;
195192 const kasPrivateKeyBytes = base64 . decodeArrayBuffer (
196193 removePemFormatting ( Mocks . kasECPrivateKey )
@@ -215,13 +212,27 @@ const kas: RequestListener = async (req, res) => {
215212 }
216213 if ( clientPublicKey . algorithm . name == 'RSA-OAEP' ) {
217214 const cek = await encryptWithPublicKey ( dek , rewrap . clientPublicKey ) ;
218- const reply = {
219- entityWrappedKey : base64 . encodeArrayBuffer ( cek . asArrayBuffer ( ) ) ,
220- metadata : { hello : 'world' } ,
221- } ;
215+ const reply = create ( RewrapResponseSchema , {
216+ responses : [
217+ create ( PolicyRewrapResultSchema , {
218+ results : [
219+ create ( KeyAccessRewrapResultSchema , {
220+ metadata : { "hello" : create ( ValueSchema , {
221+ kind : { case : "stringValue" , value : "world" }
222+ } ) } ,
223+ result : {
224+ case : "kasWrappedKey" ,
225+ value : new Uint8Array ( cek . asArrayBuffer ( ) ) ,
226+ } ,
227+ keyAccessObjectId : rewrap . requests ?. [ 0 ] ?. keyAccessObjects ?. [ 0 ] ?. keyAccessObjectId || '' ,
228+ } ) ,
229+ ] ,
230+ } ) ,
231+ ] ,
232+ } )
222233 res . statusCode = 200 ;
223234 res . setHeader ( 'Content-Type' , 'application/json' ) ;
224- res . end ( JSON . stringify ( reply ) ) ;
235+ res . end ( toJsonString ( RewrapResponseSchema , reply ) ) ;
225236 return ;
226237 }
227238 const sessionKeyPair = await crypto . subtle . generateKey (
@@ -241,19 +252,34 @@ const kas: RequestListener = async (req, res) => {
241252 const entityWrappedKey = new Uint8Array ( iv . length + cek . byteLength ) ;
242253 entityWrappedKey . set ( iv ) ;
243254 entityWrappedKey . set ( new Uint8Array ( cek ) , iv . length ) ;
244- const reply = {
245- entityWrappedKey : base64 . encodeArrayBuffer ( entityWrappedKey ) ,
246- metadata : { hello : 'world' } ,
247- } ;
255+ const reply = create ( RewrapResponseSchema , {
256+ responses : [
257+ create ( PolicyRewrapResultSchema , {
258+ results : [
259+ create ( KeyAccessRewrapResultSchema , {
260+ metadata : { "hello" : create ( ValueSchema , {
261+ kind : { case : "stringValue" , value : "world" }
262+ } ) } ,
263+ result : {
264+ case : "kasWrappedKey" ,
265+ value : entityWrappedKey ,
266+ } ,
267+ keyAccessObjectId : rewrap . requests ?. [ 0 ] ?. keyAccessObjects ?. [ 0 ] ?. keyAccessObjectId || '' ,
268+ } ) ,
269+ ] ,
270+ } ) ,
271+ ] ,
272+ } )
273+
248274 res . statusCode = 200 ;
249275 res . setHeader ( 'Content-Type' , 'application/json' ) ;
250- res . end ( JSON . stringify ( reply ) ) ;
276+ res . end ( toJsonString ( RewrapResponseSchema , reply ) ) ;
251277 return ;
252278 }
253279 // nanotdf
254280 console . log ( '[INFO] nano rewrap request body: ' , rewrap ) ;
255281 const { header } = Header . parse (
256- new Uint8Array ( base64 . decodeArrayBuffer ( rewrap ?. keyAccess ?. header || '' ) )
282+ kaoheader || new Uint8Array ( base64 . decodeArrayBuffer ( '' ) ) ,
257283 ) ;
258284 // TODO convert header.ephemeralCurveName to namedCurve
259285 const nanoPublicKey = await crypto . subtle . importKey (
@@ -309,14 +335,29 @@ const kas: RequestListener = async (req, res) => {
309335 const entityWrappedKey = new Uint8Array ( iv . length + cekBytes . length ) ;
310336 entityWrappedKey . set ( iv ) ;
311337 entityWrappedKey . set ( cekBytes , iv . length ) ;
312- const reply = {
313- entityWrappedKey : base64 . encodeArrayBuffer ( entityWrappedKey ) ,
338+ const reply = create ( RewrapResponseSchema , {
314339 sessionPublicKey : Mocks . kasECCert ,
315- metadata : { hello : 'people of earth' } ,
316- } ;
340+ responses : [
341+ create ( PolicyRewrapResultSchema , {
342+ results : [
343+ create ( KeyAccessRewrapResultSchema , {
344+ metadata : { "hello" : create ( ValueSchema , {
345+ kind : { case : "stringValue" , value : "people of earth" }
346+ } ) } ,
347+ result : {
348+ case : "kasWrappedKey" ,
349+ value : entityWrappedKey ,
350+ } ,
351+ keyAccessObjectId : rewrap . requests ?. [ 0 ] ?. keyAccessObjects ?. [ 0 ] ?. keyAccessObjectId || '' ,
352+ } ) ,
353+ ] ,
354+ } ) ,
355+ ] ,
356+ } ) ;
357+
317358 res . statusCode = 200 ;
318359 res . setHeader ( 'Content-Type' , 'application/json' ) ;
319- res . end ( JSON . stringify ( reply ) ) ;
360+ res . end ( toJsonString ( RewrapResponseSchema , reply ) ) ;
320361 return ;
321362 } else if ( url . pathname === '/file' ) {
322363 if ( req . method !== 'GET' ) {
0 commit comments