Skip to content

Commit

Permalink
Merge pull request #169 from andrewwhitehead/js/upd
Browse files Browse the repository at this point in the history
Add profile management and store export to JS wrapper
  • Loading branch information
andrewwhitehead authored Sep 8, 2023
2 parents ee46c20 + 24afabe commit b9f004c
Show file tree
Hide file tree
Showing 18 changed files with 470 additions and 6 deletions.
2 changes: 1 addition & 1 deletion askar-crypto/src/alg/p384.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ impl KeySigVerify for P384KeyPair {
sig_type: Option<SignatureType>,
) -> Result<bool, Error> {
match sig_type {
None | Some(SignatureType::ES256) => Ok(self.verify_signature(message, signature)),
None | Some(SignatureType::ES384) => Ok(self.verify_signature(message, signature)),
#[allow(unreachable_patterns)]
_ => Err(err_msg!(Unsupported, "Unsupported signature type")),
}
Expand Down
37 changes: 37 additions & 0 deletions include/libaries_askar.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ typedef struct FfiResultList_Entry FfiResultList_Entry;

typedef struct FfiResultList_KeyEntry FfiResultList_KeyEntry;

typedef struct FfiResultList_String FfiResultList_String;

/**
* A stored key entry
*/
Expand Down Expand Up @@ -221,6 +223,14 @@ typedef int64_t CallbackId;

typedef void (*LogCallback)(const void *context, int32_t level, const char *target, const char *message, const char *module_path, const char *file, int32_t line);

typedef struct FfiResultList_String FfiStringList;

typedef struct ArcHandle_FfiStringList {
const FfiStringList *_0;
} ArcHandle_FfiStringList;

typedef struct ArcHandle_FfiStringList StringListHandle;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
Expand Down Expand Up @@ -524,17 +534,33 @@ ErrorCode askar_store_close(StoreHandle handle,
void (*cb)(CallbackId cb_id, ErrorCode err),
CallbackId cb_id);

ErrorCode askar_store_copy(StoreHandle handle,
FfiStr target_uri,
FfiStr key_method,
FfiStr pass_key,
int8_t recreate,
void (*cb)(CallbackId cb_id, ErrorCode err, StoreHandle handle),
CallbackId cb_id);

ErrorCode askar_store_create_profile(StoreHandle handle,
FfiStr profile,
void (*cb)(CallbackId cb_id, ErrorCode err, const char *result_p),
CallbackId cb_id);

ErrorCode askar_store_generate_raw_key(struct ByteBuffer seed, const char **out);

ErrorCode askar_store_get_default_profile(StoreHandle handle,
void (*cb)(CallbackId cb_id, ErrorCode err, const char *profile),
CallbackId cb_id);

ErrorCode askar_store_get_profile_name(StoreHandle handle,
void (*cb)(CallbackId cb_id, ErrorCode err, const char *name),
CallbackId cb_id);

ErrorCode askar_store_list_profiles(StoreHandle handle,
void (*cb)(CallbackId cb_id, ErrorCode err, StringListHandle results),
CallbackId cb_id);

ErrorCode askar_store_open(FfiStr spec_uri,
FfiStr key_method,
FfiStr pass_key,
Expand Down Expand Up @@ -565,6 +591,17 @@ ErrorCode askar_store_remove_profile(StoreHandle handle,
void (*cb)(CallbackId cb_id, ErrorCode err, int8_t removed),
CallbackId cb_id);

ErrorCode askar_store_set_default_profile(StoreHandle handle,
FfiStr profile,
void (*cb)(CallbackId cb_id, ErrorCode err),
CallbackId cb_id);

ErrorCode askar_string_list_count(StringListHandle handle, int32_t *count);

void askar_string_list_free(StringListHandle handle);

ErrorCode askar_string_list_get_item(StringListHandle handle, int32_t index, const char **item);

void askar_terminate(void);

char *askar_version(void);
Expand Down
77 changes: 77 additions & 0 deletions tests/store_copy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use aries_askar::{
future::block_on,
kms::{KeyAlg, LocalKey},
Store, StoreKeyMethod,
};

const ERR_RAW_KEY: &str = "Error creating raw store key";
const ERR_SESSION: &str = "Error creating store session";
const ERR_OPEN: &str = "Error opening test store instance";
const ERR_REQ_ROW: &str = "Row required";
const ERR_CLOSE: &str = "Error closing test store instance";

#[test]
fn store_copy() {
block_on(async {
let pass_key = Store::new_raw_key(None).expect(ERR_RAW_KEY);
let db = Store::provision(
"sqlite://:memory:",
StoreKeyMethod::RawKey,
pass_key,
None,
true,
)
.await
.expect(ERR_OPEN);

let keypair = LocalKey::generate(KeyAlg::Ed25519, false).expect("Error creating keypair");

let mut conn = db.session(None).await.expect(ERR_SESSION);

let key_name = "testkey";
let metadata = "meta";
conn.insert_key(key_name, &keypair, Some(metadata), None, None)
.await
.expect("Error inserting key");

let row_cat = "testcat";
let row_name = "testrow";
let row_value = "testval";
conn.insert(row_cat, row_name, row_value.as_bytes(), None, None)
.await
.expect("Error inserting row");

drop(conn);

let pass_key_copy = Store::new_raw_key(None).expect(ERR_RAW_KEY);
let copied = db
.copy_to(
"sqlite://:memory:",
StoreKeyMethod::RawKey,
pass_key_copy,
true,
)
.await
.expect("Error copying store");

let mut conn = copied.session(None).await.expect(ERR_SESSION);
let found = conn
.fetch_key(key_name, false)
.await
.expect("Error fetching key")
.expect(ERR_REQ_ROW);
assert_eq!(found.algorithm(), Some(KeyAlg::Ed25519.as_str()));
assert_eq!(found.name(), key_name);
assert_eq!(found.metadata(), Some(metadata));
assert!(found.is_local());
found.load_local_key().expect("Error loading key");

let found = conn
.fetch(row_cat, row_name, false)
.await
.expect("Error loading row");
assert!(found.is_some());

db.close().await.expect(ERR_CLOSE);
})
}
54 changes: 54 additions & 0 deletions wrappers/javascript/aries-askar-nodejs/src/NodeJSAriesAskar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import type {
SetCustomLoggerOptions,
SetMaxLogLevelOptions,
StoreCloseOptions,
StoreCopyToOptions,
StoreCreateProfileOptions,
StoreGenerateRawKeyOptions,
StoreGetProfileNameOptions,
Expand All @@ -80,6 +81,9 @@ import type {
AriesAskarErrorObject,
AeadParamsOptions,
MigrateIndySdkOptions,
StoreGetDefaultProfileOptions,
StoreSetDefaultProfileOptions,
StoreListProfilesOptions,
} from '@hyperledger/aries-askar-shared'

import {
Expand Down Expand Up @@ -117,6 +121,7 @@ import {
FFI_SESSION_HANDLE,
FFI_STORE_HANDLE,
FFI_INT8,
FFI_STRING_LIST_HANDLE,
} from './ffi'
import { getNativeAriesAskar } from './library'

Expand Down Expand Up @@ -902,6 +907,14 @@ export class NodeJSAriesAskar implements AriesAskar {
return this.promisify((cb, cbId) => this.nativeAriesAskar.askar_store_close(storeHandle, cb, cbId))
}

public storeCopyTo(options: StoreCopyToOptions): Promise<void> {
const { storeHandle, targetUri, passKey, keyMethod, recreate } = serializeArguments(options)

return this.promisify((cb, cbId) =>
this.nativeAriesAskar.askar_store_copy(storeHandle, targetUri, keyMethod, passKey, recreate, cb, cbId)
)
}

public async storeCreateProfile(options: StoreCreateProfileOptions): Promise<string> {
const { storeHandle, profile } = serializeArguments(options)
const response = await this.promisifyWithResponse<string>(
Expand All @@ -922,6 +935,15 @@ export class NodeJSAriesAskar implements AriesAskar {
return ret.deref() as string
}

public async storeGetDefaultProfile(options: StoreGetDefaultProfileOptions): Promise<string> {
const { storeHandle } = serializeArguments(options)
const response = await this.promisifyWithResponse<string>((cb, cbId) =>
this.nativeAriesAskar.askar_store_get_default_profile(storeHandle, cb, cbId)
)

return handleInvalidNullResponse(response)
}

public async storeGetProfileName(options: StoreGetProfileNameOptions): Promise<string> {
const { storeHandle } = serializeArguments(options)
const response = await this.promisifyWithResponse<string>((cb, cbId) =>
Expand All @@ -931,6 +953,30 @@ export class NodeJSAriesAskar implements AriesAskar {
return handleInvalidNullResponse(response)
}

public async storeListProfiles(options: StoreListProfilesOptions): Promise<string[]> {
const { storeHandle } = serializeArguments(options)
const listHandle = await this.promisifyWithResponse<Buffer>(
(cb, cbId) => this.nativeAriesAskar.askar_store_list_profiles(storeHandle, cb, cbId),
FFI_STRING_LIST_HANDLE
)
if (listHandle === null) {
throw AriesAskarError.customError({ message: 'Invalid handle' })
}
const counti32 = allocateInt32Buffer()
this.nativeAriesAskar.askar_string_list_count(listHandle, counti32)
this.handleError()
const count = counti32.deref() as number
const ret = []
const strval = allocateStringBuffer()
for (let i = 0; i < count; i++) {
this.nativeAriesAskar.askar_string_list_get_item(listHandle, i, strval)
this.handleError()
ret.push(strval.deref() as string)
}
this.nativeAriesAskar.askar_string_list_free(listHandle)
return ret
}

public async storeOpen(options: StoreOpenOptions): Promise<StoreHandle> {
const { profile, keyMethod, passKey, specUri } = serializeArguments(options)

Expand Down Expand Up @@ -983,6 +1029,14 @@ export class NodeJSAriesAskar implements AriesAskar {
return handleInvalidNullResponse(response)
}

public async storeSetDefaultProfile(options: StoreSetDefaultProfileOptions): Promise<void> {
const { storeHandle, profile } = serializeArguments(options)

return this.promisify((cb, cbId) =>
this.nativeAriesAskar.askar_store_set_default_profile(storeHandle, profile, cb, cbId)
)
}

public async migrateIndySdk(options: MigrateIndySdkOptions): Promise<void> {
const { specUri, kdfLevel, walletKey, walletName } = serializeArguments(options)
await this.promisify((cb, cbId) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ export const FFI_LOCAL_KEY_HANDLE = FFI_ARC_HANDLE
export const FFI_SESSION_HANDLE = FFI_USIZE
export const FFI_SCAN_HANDLE = FFI_USIZE
export const FFI_STORE_HANDLE = FFI_USIZE
export const FFI_STRING_LIST_HANDLE = FFI_ARC_HANDLE
12 changes: 12 additions & 0 deletions wrappers/javascript/aries-askar-nodejs/src/library/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
FFI_STORE_HANDLE,
FFI_STRING,
FFI_STRING_PTR,
FFI_STRING_LIST_HANDLE,
SecretBufferStruct,
SecretBufferStructPtr,
ByteBufferStruct,
Expand All @@ -42,6 +43,10 @@ export const nativeBindings = {
askar_entry_list_get_tags: [FFI_ERROR_CODE, [FFI_ENTRY_LIST_HANDLE, FFI_INT32, FFI_STRING_PTR]],
askar_entry_list_get_value: [FFI_ERROR_CODE, [FFI_ENTRY_LIST_HANDLE, FFI_INT32, SecretBufferStructPtr]],

askar_string_list_count: [FFI_ERROR_CODE, [FFI_STRING_LIST_HANDLE, FFI_INT32_PTR]],
askar_string_list_free: [FFI_ERROR_CODE, [FFI_STRING_LIST_HANDLE]],
askar_string_list_get_item: [FFI_ERROR_CODE, [FFI_STRING_LIST_HANDLE, FFI_INT32, FFI_STRING_PTR]],

askar_key_aead_decrypt: [
FFI_ERROR_CODE,
[FFI_POINTER, ByteBufferStruct, ByteBufferStruct, ByteBufferStruct, ByteBufferStruct, SecretBufferStructPtr],
Expand Down Expand Up @@ -183,9 +188,15 @@ export const nativeBindings = {
],

askar_store_close: [FFI_ERROR_CODE, [FFI_STORE_HANDLE, FFI_CALLBACK_PTR, FFI_CALLBACK_ID]],
askar_store_copy: [
FFI_ERROR_CODE,
[FFI_STORE_HANDLE, FFI_STRING, FFI_STRING, FFI_STRING, FFI_INT8, FFI_CALLBACK_PTR, FFI_CALLBACK_ID],
],
askar_store_create_profile: [FFI_ERROR_CODE, [FFI_STORE_HANDLE, FFI_STRING, FFI_CALLBACK_PTR, FFI_CALLBACK_ID]],
askar_store_generate_raw_key: [FFI_ERROR_CODE, [ByteBufferStruct, FFI_STRING_PTR]],
askar_store_get_profile_name: [FFI_ERROR_CODE, [FFI_STORE_HANDLE, FFI_CALLBACK_PTR, FFI_CALLBACK_ID]],
askar_store_get_default_profile: [FFI_ERROR_CODE, [FFI_STORE_HANDLE, FFI_CALLBACK_PTR, FFI_CALLBACK_ID]],
askar_store_list_profiles: [FFI_ERROR_CODE, [FFI_STORE_HANDLE, FFI_CALLBACK_PTR, FFI_CALLBACK_ID]],
askar_store_open: [
FFI_ERROR_CODE,
[FFI_STRING, FFI_STRING, FFI_STRING, FFI_STRING, FFI_CALLBACK_PTR, FFI_CALLBACK_ID],
Expand All @@ -197,6 +208,7 @@ export const nativeBindings = {
askar_store_rekey: [FFI_ERROR_CODE, [FFI_STORE_HANDLE, FFI_STRING, FFI_STRING, FFI_CALLBACK_PTR, FFI_CALLBACK_ID]],
askar_store_remove: [FFI_ERROR_CODE, [FFI_STRING, FFI_CALLBACK_PTR, FFI_CALLBACK_ID]],
askar_store_remove_profile: [FFI_ERROR_CODE, [FFI_STORE_HANDLE, FFI_STRING, FFI_CALLBACK_PTR, FFI_CALLBACK_ID]],
askar_store_set_default_profile: [FFI_ERROR_CODE, [FFI_STORE_HANDLE, FFI_STRING, FFI_CALLBACK_PTR, FFI_CALLBACK_ID]],

askar_migrate_indy_sdk: [
FFI_ERROR_CODE,
Expand Down
18 changes: 18 additions & 0 deletions wrappers/javascript/aries-askar-nodejs/tests/keys.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,21 @@ describe('keys', () => {
})
})
})

test('p384', () => {
const key = Key.generate(KeyAlgs.EcSecp384r1)
expect(key.algorithm).toStrictEqual(KeyAlgs.EcSecp384r1)
const message = Uint8Array.from(Buffer.from('test message'))
const signature = key.signMessage({ message })
expect(key.verifySignature({ message, signature })).toStrictEqual(true)

expect(key.jwkPublic).toMatchObject({
kty: 'EC',
crv: 'P-384',
})

expect(key.jwkSecret).toMatchObject({
kty: 'EC',
crv: 'P-384',
})
})
18 changes: 17 additions & 1 deletion wrappers/javascript/aries-askar-nodejs/tests/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ describe('Store and Session', () => {
found.forEach((entry) => entry.key.handle.free())
})

test('profile', async () => {
test('Profile', async () => {
const session = await store.openSession()
await session.insert(firstEntry)
await session.close()
Expand Down Expand Up @@ -258,6 +258,11 @@ describe('Store and Session', () => {
await expect(session4.count(firstEntry)).resolves.toStrictEqual(1)
await session4.close()

await store.setDefaultProfile(profile)
await expect(store.getDefaultProfile()).resolves.toStrictEqual(profile)

await expect(store.listProfiles()).resolves.toContain(profile)

await store.removeProfile(profile)

// Profile key is cached
Expand All @@ -274,4 +279,15 @@ describe('Store and Session', () => {
await expect(session7.count(firstEntry)).resolves.toStrictEqual(0)
await session7.close()
})

test('Copy', async () => {
const key = getRawKey()

await store.copyTo({
uri: 'sqlite://:memory:',
keyMethod: new StoreKeyMethod(KdfMethod.Raw),
passKey: key,
recreate: true,
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ FunctionMap AriesAskarTurboModuleHostObject::functionMapping(jsi::Runtime &rt) {
fMap.insert(
std::make_tuple("setDefaultLogger", &ariesAskar::setDefaultLogger));

fMap.insert(std::make_tuple("storeCopyTo", &ariesAskar::storeCopyTo));
fMap.insert(std::make_tuple("storeOpen", &ariesAskar::storeOpen));
fMap.insert(
std::make_tuple("storeGenerateRawKey", &ariesAskar::storeGenerateRawKey));
Expand All @@ -26,11 +27,17 @@ FunctionMap AriesAskarTurboModuleHostObject::functionMapping(jsi::Runtime &rt) {
std::make_tuple("storeGenerateRawKey", &ariesAskar::storeGenerateRawKey));
fMap.insert(
std::make_tuple("storeGetProfileName", &ariesAskar::storeGetProfileName));
fMap.insert(
std::make_tuple("storeGetDefaultProfile",
&ariesAskar::storeGetDefaultProfile));
fMap.insert(std::make_tuple("storeProvision", &ariesAskar::storeProvision));
fMap.insert(std::make_tuple("storeRekey", &ariesAskar::storeRekey));
fMap.insert(std::make_tuple("storeRemove", &ariesAskar::storeRemove));
fMap.insert(
std::make_tuple("storeRemoveProfile", &ariesAskar::storeRemoveProfile));
fMap.insert(
std::make_tuple("storeSetDefaultProfile",
&ariesAskar::storeSetDefaultProfile));

fMap.insert(std::make_tuple("sessionClose", &ariesAskar::sessionClose));
fMap.insert(std::make_tuple("sessionCount", &ariesAskar::sessionCount));
Expand Down
Loading

0 comments on commit b9f004c

Please sign in to comment.