crypto update

This commit is contained in:
John Smith 2023-05-16 21:34:34 -04:00
parent 8660457f95
commit 9a43faaf10
10 changed files with 366 additions and 17 deletions

View File

@ -16,11 +16,8 @@ pub trait CryptoSystem {
fn random_bytes(&self, len: u32) -> Vec<u8>;
fn default_salt_length(&self) -> u32;
fn hash_password(&self, password: &[u8], salt: &[u8]) -> Result<String, VeilidAPIError>;
fn verify_password(
&self,
password: &[u8],
password_hash: String,
) -> Result<bool, VeilidAPIError>;
fn verify_password(&self, password: &[u8], password_hash: &str)
-> Result<bool, VeilidAPIError>;
fn derive_shared_secret(
&self,
password: &[u8],

View File

@ -103,7 +103,7 @@ impl CryptoSystem for CryptoSystemNONE {
fn verify_password(
&self,
password: &[u8],
password_hash: String,
password_hash: &str,
) -> Result<bool, VeilidAPIError> {
let Some((salt, _)) = password_hash.split_once(":") else {
apibail_generic!("invalid format");
@ -111,7 +111,7 @@ impl CryptoSystem for CryptoSystemNONE {
let Ok(salt) = BASE64URL_NOPAD.decode(salt.as_bytes()) else {
apibail_generic!("invalid salt");
};
return Ok(self.hash_password(password, &salt)? == password_hash);
return Ok(&self.hash_password(password, &salt)? == password_hash);
}
fn derive_shared_secret(

View File

@ -195,11 +195,11 @@ pub async fn test_generation(vcrypto: CryptoSystemVersion) {
)
.expect_err("should reject long salt");
assert!(vcrypto.verify_password(b"abc123", pstr1.clone()).unwrap());
assert!(vcrypto.verify_password(b"abc123", pstr2.clone()).unwrap());
assert!(vcrypto.verify_password(b"abc123", pstr3.clone()).unwrap());
assert!(!vcrypto.verify_password(b"abc123", pstr4.clone()).unwrap());
assert!(!vcrypto.verify_password(b"abc123", pstr5.clone()).unwrap());
assert!(vcrypto.verify_password(b"abc123", &pstr1).unwrap());
assert!(vcrypto.verify_password(b"abc123", &pstr2).unwrap());
assert!(vcrypto.verify_password(b"abc123", &pstr3).unwrap());
assert!(!vcrypto.verify_password(b"abc123", &pstr4).unwrap());
assert!(!vcrypto.verify_password(b"abc123", &pstr5).unwrap());
let ss1 = vcrypto.derive_shared_secret(b"abc123", b"qwerasdf");
let ss2 = vcrypto.derive_shared_secret(b"abc123", b"qwerasdf");

View File

@ -104,9 +104,9 @@ impl CryptoSystem for CryptoSystemVLD0 {
fn verify_password(
&self,
password: &[u8],
password_hash: String,
password_hash: &str,
) -> Result<bool, VeilidAPIError> {
let parsed_hash = PasswordHash::new(&password_hash).map_err(VeilidAPIError::generic)?;
let parsed_hash = PasswordHash::new(password_hash).map_err(VeilidAPIError::generic)?;
// Argon2 with default params (Argon2id v19)
let argon2 = Argon2::default();

View File

@ -32,7 +32,6 @@
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols

View File

@ -141,6 +141,11 @@ abstract class VeilidCryptoSystem {
CryptoKind kind();
Future<SharedSecret> cachedDH(PublicKey key, SecretKey secret);
Future<SharedSecret> computeDH(PublicKey key, SecretKey secret);
Future<Uint8List> randomBytes(int len);
Future<int> defaultSaltLength();
Future<String> hashPassword(Uint8List password, Uint8List salt);
Future<bool> verifyPassword(Uint8List password, String passwordHash);
Future<SharedSecret> deriveSharedSecret(Uint8List password, Uint8List salt);
Future<Nonce> randomNonce();
Future<SharedSecret> randomSharedSecret();
Future<KeyPair> generateKeyPair();

View File

@ -213,6 +213,28 @@ typedef _CryptoComputeDHC = Void Function(
Int64, Uint32, Pointer<Utf8>, Pointer<Utf8>);
typedef _CryptoComputeDHDart = void Function(
int, int, Pointer<Utf8>, Pointer<Utf8>);
// fn crypto_random_bytes(port: i64, kind: u32, len: u32)
typedef _CryptoRandomBytesC = Void Function(Int64, Uint32, Uint32);
typedef _CryptoRandomBytesDart = void Function(int, int, int);
// fn crypto_default_salt_length(port: i64, kind: u32)
typedef _CryptoDefaultSaltLengthC = Void Function(Int64, Uint32);
typedef _CryptoDefaultSaltLengthDart = void Function(int, int);
// fn crypto_hash_password(port: i64, kind: u32, password: FfiStr, salt: FfiStr )
typedef _CryptoHashPasswordC = Void Function(
Int64, Uint32, Pointer<Utf8>, Pointer<Utf8>);
typedef _CryptoHashPasswordDart = void Function(
int, int, Pointer<Utf8>, Pointer<Utf8>);
// fn crypto_verify_password(port: i64, kind: u32, password: FfiStr, password_hash: FfiStr )
typedef _CryptoVerifyPasswordC = Void Function(
Int64, Uint32, Pointer<Utf8>, Pointer<Utf8>);
typedef _CryptoVerifyPasswordDart = void Function(
int, int, Pointer<Utf8>, Pointer<Utf8>);
// fn crypto_derive_shared_secret(port: i64, kind: u32, password: FfiStr, salt: FfiStr )
typedef _CryptoDeriveSharedSecretC = Void Function(
Int64, Uint32, Pointer<Utf8>, Pointer<Utf8>);
typedef _CryptoDeriveSharedSecretDart = void Function(
int, int, Pointer<Utf8>, Pointer<Utf8>);
// fn crypto_random_nonce(port: i64, kind: u32)
typedef _CryptoRandomNonceC = Void Function(Int64, Uint32);
typedef _CryptoRandomNonceDart = void Function(int, int);
@ -884,7 +906,7 @@ class VeilidTableDBFFI extends VeilidTableDB {
// FFI implementation of VeilidCryptoSystem
class VeilidCryptoSystemFFI implements VeilidCryptoSystem {
final CryptoKind _kind;
VeilidFFI _ffi;
final VeilidFFI _ffi;
VeilidCryptoSystemFFI._(this._ffi, this._kind);
@ -915,6 +937,59 @@ class VeilidCryptoSystemFFI implements VeilidCryptoSystem {
return processFutureJson(SharedSecret.fromJson, recvPort.first);
}
@override
Future<Uint8List> randomBytes(int len) async {
final recvPort = ReceivePort("crypto_random_bytes");
final sendPort = recvPort.sendPort;
_ffi._cryptoRandomBytes(sendPort.nativePort, _kind, len);
final out = await processFuturePlain(recvPort.first);
return base64UrlNoPadDecode(out);
}
@override
Future<int> defaultSaltLength() {
final recvPort = ReceivePort("crypto_default_salt_length");
final sendPort = recvPort.sendPort;
_ffi._cryptoDefaultSaltLength(sendPort.nativePort, _kind);
return processFuturePlain(recvPort.first);
}
@override
Future<String> hashPassword(Uint8List password, Uint8List salt) {
final nativeEncodedPassword = base64UrlNoPadEncode(password).toNativeUtf8();
final nativeEncodedSalt = base64UrlNoPadEncode(salt).toNativeUtf8();
final recvPort = ReceivePort("crypto_hash_password");
final sendPort = recvPort.sendPort;
_ffi._cryptoHashPassword(
sendPort.nativePort, _kind, nativeEncodedPassword, nativeEncodedSalt);
return processFuturePlain(recvPort.first);
}
@override
Future<bool> verifyPassword(Uint8List password, String passwordHash) {
final nativeEncodedPassword = base64UrlNoPadEncode(password).toNativeUtf8();
final nativeEncodedPasswordHash = passwordHash.toNativeUtf8();
final recvPort = ReceivePort("crypto_verify_password");
final sendPort = recvPort.sendPort;
_ffi._cryptoVerifyPassword(sendPort.nativePort, _kind,
nativeEncodedPassword, nativeEncodedPasswordHash);
return processFuturePlain(recvPort.first);
}
@override
Future<SharedSecret> deriveSharedSecret(Uint8List password, Uint8List salt) {
final nativeEncodedPassword = base64UrlNoPadEncode(password).toNativeUtf8();
final nativeEncodedSalt = base64UrlNoPadEncode(salt).toNativeUtf8();
final recvPort = ReceivePort("crypto_derive_shared_secret");
final sendPort = recvPort.sendPort;
_ffi._cryptoHashPassword(
sendPort.nativePort, _kind, nativeEncodedPassword, nativeEncodedSalt);
return processFutureJson(SharedSecret.fromJson, recvPort.first);
}
@override
Future<Nonce> randomNonce() {
final recvPort = ReceivePort("crypto_random_nonce");
@ -1134,6 +1209,13 @@ class VeilidFFI implements Veilid {
final _CryptoCachedDHDart _cryptoCachedDH;
final _CryptoComputeDHDart _cryptoComputeDH;
final _CryptoRandomBytesDart _cryptoRandomBytes;
final _CryptoDefaultSaltLengthDart _cryptoDefaultSaltLength;
final _CryptoHashPasswordDart _cryptoHashPassword;
final _CryptoVerifyPasswordDart _cryptoVerifyPassword;
final _CryptoDeriveSharedSecretDart _cryptoDeriveSharedSecret;
final _CryptoRandomNonceDart _cryptoRandomNonce;
final _CryptoRandomSharedSecretDart _cryptoRandomSharedSecret;
final _CryptoGenerateKeyPairDart _cryptoGenerateKeyPair;
@ -1295,6 +1377,20 @@ class VeilidFFI implements Veilid {
_cryptoComputeDH =
dylib.lookupFunction<_CryptoComputeDHC, _CryptoComputeDHDart>(
'crypto_compute_dh'),
_cryptoRandomBytes =
dylib.lookupFunction<_CryptoRandomBytesC, _CryptoRandomBytesDart>(
'crypto_random_bytes'),
_cryptoDefaultSaltLength = dylib.lookupFunction<
_CryptoDefaultSaltLengthC,
_CryptoDefaultSaltLengthDart>('crypto_default_salt_length'),
_cryptoHashPassword =
dylib.lookupFunction<_CryptoHashPasswordC, _CryptoHashPasswordDart>(
'crypto_hash_password'),
_cryptoVerifyPassword = dylib.lookupFunction<_CryptoVerifyPasswordC,
_CryptoVerifyPasswordDart>('crypto_verify_password'),
_cryptoDeriveSharedSecret = dylib.lookupFunction<
_CryptoDeriveSharedSecretC,
_CryptoVerifyPasswordDart>('crypto_derive_shared_secret'),
_cryptoRandomNonce =
dylib.lookupFunction<_CryptoRandomNonceC, _CryptoRandomNonceDart>(
'crypto_random_nonce'),

View File

@ -157,7 +157,10 @@ class VeilidCryptoSystemJS implements VeilidCryptoSystem {
final CryptoKind _kind;
final VeilidJS _js;
VeilidCryptoSystemJS._(this._js, this._kind);
VeilidCryptoSystemJS._(this._js, this._kind) {
// Keep the reference
_js;
}
@override
CryptoKind kind() {
@ -178,6 +181,41 @@ class VeilidCryptoSystemJS implements VeilidCryptoSystem {
[_kind, jsonEncode(key), jsonEncode(secret)]))));
}
@override
Future<Uint8List> randomBytes(int len) async {
return base64UrlNoPadDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "crypto_random_bytes", [_kind, len])));
}
@override
Future<int> defaultSaltLength() {
return _wrapApiPromise(
js_util.callMethod(wasm, "crypto_default_salt_length", [_kind]));
}
@override
Future<String> hashPassword(Uint8List password, Uint8List salt) {
return _wrapApiPromise(js_util.callMethod(wasm, "crypto_hash_password",
[_kind, base64UrlNoPadEncode(password), base64UrlNoPadEncode(salt)]));
}
@override
Future<bool> verifyPassword(Uint8List password, String passwordHash) {
return _wrapApiPromise(js_util.callMethod(wasm, "crypto_verify_password",
[_kind, base64UrlNoPadEncode(password), passwordHash]));
}
@override
Future<SharedSecret> deriveSharedSecret(
Uint8List password, Uint8List salt) async {
return SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(wasm, "crypto_derive_shared_secret", [
_kind,
base64UrlNoPadEncode(password),
base64UrlNoPadEncode(salt)
]))));
}
@override
Future<Nonce> randomNonce() async {
return Nonce.fromJson(jsonDecode(await _wrapApiPromise(

View File

@ -1091,6 +1091,109 @@ pub extern "C" fn crypto_compute_dh(port: i64, kind: u32, key: FfiStr, secret: F
});
}
#[no_mangle]
pub extern "C" fn crypto_random_bytes(port: i64, kind: u32, len: u32) {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);
DartIsolateWrapper::new(port).spawn_result(async move {
let veilid_api = get_veilid_api().await?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_random_bytes", "kind", kind.to_string()))?;
let out = csv.random_bytes(len);
let out = data_encoding::BASE64URL_NOPAD.encode(&out);
APIResult::Ok(out)
});
}
#[no_mangle]
pub extern "C" fn crypto_default_salt_length(port: i64, kind: u32) {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);
DartIsolateWrapper::new(port).spawn_result(async move {
let veilid_api = get_veilid_api().await?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_default_salt_length", "kind", kind.to_string()))?;
let out = csv.default_salt_length();
APIResult::Ok(out)
});
}
#[no_mangle]
pub extern "C" fn crypto_hash_password(port: i64, kind: u32, password: FfiStr, salt: FfiStr ) {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);
let password: Vec<u8> = data_encoding::BASE64URL_NOPAD
.decode(
password.into_opt_string()
.unwrap()
.as_bytes(),
)
.unwrap();
let salt: Vec<u8> = data_encoding::BASE64URL_NOPAD
.decode(
salt.into_opt_string()
.unwrap()
.as_bytes(),
)
.unwrap();
DartIsolateWrapper::new(port).spawn_result(async move {
let veilid_api = get_veilid_api().await?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_hash_password", "kind", kind.to_string()))?;
let out = csv.hash_password(&password, &salt)?;
APIResult::Ok(out)
});
}
#[no_mangle]
pub extern "C" fn crypto_verify_password(port: i64, kind: u32, password: FfiStr, password_hash: FfiStr ) {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);
let password: Vec<u8> = data_encoding::BASE64URL_NOPAD
.decode(
password.into_opt_string()
.unwrap()
.as_bytes(),
)
.unwrap();
let password_hash = password_hash.into_opt_string().unwrap();
DartIsolateWrapper::new(port).spawn_result(async move {
let veilid_api = get_veilid_api().await?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_verify_password", "kind", kind.to_string()))?;
let out = csv.verify_password(&password, &password_hash)?;
APIResult::Ok(out)
});
}
#[no_mangle]
pub extern "C" fn crypto_derive_shared_secret(port: i64, kind: u32, password: FfiStr, salt: FfiStr ) {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);
let password: Vec<u8> = data_encoding::BASE64URL_NOPAD
.decode(
password.into_opt_string()
.unwrap()
.as_bytes(),
)
.unwrap();
let salt: Vec<u8> = data_encoding::BASE64URL_NOPAD
.decode(
salt.into_opt_string()
.unwrap()
.as_bytes(),
)
.unwrap();
DartIsolateWrapper::new(port).spawn_result_json(async move {
let veilid_api = get_veilid_api().await?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_derive_shared_secret", "kind", kind.to_string()))?;
let out = csv.derive_shared_secret(&password, &salt)?;
APIResult::Ok(out)
});
}
#[no_mangle]
pub extern "C" fn crypto_random_nonce(port: i64, kind: u32) {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);

View File

@ -1014,6 +1014,117 @@ pub fn crypto_compute_dh(kind: u32, key: String, secret: String) -> Promise {
})
}
#[wasm_bindgen()]
pub fn crypto_random_bytes(kind: u32, len: u32) -> Promise {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);
wrap_api_future_plain(async move {
let veilid_api = get_veilid_api()?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| {
veilid_core::VeilidAPIError::invalid_argument(
"crypto_random_bytes",
"kind",
kind.to_string(),
)
})?;
let out = csv.random_bytes(len);
let out = data_encoding::BASE64URL_NOPAD.encode(&out);
APIResult::Ok(out)
})
}
#[wasm_bindgen()]
pub fn crypto_default_salt_length(kind: u32) -> Promise {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);
wrap_api_future_plain(async move {
let veilid_api = get_veilid_api()?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| {
veilid_core::VeilidAPIError::invalid_argument(
"crypto_default_salt_length",
"kind",
kind.to_string(),
)
})?;
let out = csv.default_salt_length();
APIResult::Ok(out)
})
}
#[wasm_bindgen()]
pub fn crypto_hash_password(kind: u32, password: String, salt: String) -> Promise {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);
let password: Vec<u8> = data_encoding::BASE64URL_NOPAD
.decode(password.as_bytes())
.unwrap();
let salt: Vec<u8> = data_encoding::BASE64URL_NOPAD
.decode(salt.as_bytes())
.unwrap();
wrap_api_future_plain(async move {
let veilid_api = get_veilid_api()?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| {
veilid_core::VeilidAPIError::invalid_argument(
"crypto_hash_password",
"kind",
kind.to_string(),
)
})?;
let out = csv.hash_password(&password, &salt)?;
APIResult::Ok(out)
})
}
#[wasm_bindgen()]
pub fn crypto_verify_password(kind: u32, password: String, password_hash: String) -> Promise {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);
let password: Vec<u8> = data_encoding::BASE64URL_NOPAD
.decode(password.as_bytes())
.unwrap();
wrap_api_future_plain(async move {
let veilid_api = get_veilid_api()?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| {
veilid_core::VeilidAPIError::invalid_argument(
"crypto_verify_password",
"kind",
kind.to_string(),
)
})?;
let out = csv.verify_password(&password, &password_hash)?;
APIResult::Ok(out)
})
}
#[wasm_bindgen()]
pub fn crypto_derive_shared_secret(kind: u32, password: String, salt: String) -> Promise {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);
let password: Vec<u8> = data_encoding::BASE64URL_NOPAD
.decode(password.as_bytes())
.unwrap();
let salt: Vec<u8> = data_encoding::BASE64URL_NOPAD
.decode(salt.as_bytes())
.unwrap();
wrap_api_future_json(async move {
let veilid_api = get_veilid_api()?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| {
veilid_core::VeilidAPIError::invalid_argument(
"crypto_derive_shared_secret",
"kind",
kind.to_string(),
)
})?;
let out = csv.derive_shared_secret(&password, &salt)?;
APIResult::Ok(out)
})
}
#[wasm_bindgen()]
pub fn crypto_random_nonce(kind: u32) -> Promise {
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind);