Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add profile management and store export to JS wrapper #169

Merged
merged 7 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading