Skip to content

Commit

Permalink
feat: crypto_box_seal (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippeauriach authored Jul 10, 2024
1 parent ca1ad4d commit e934d3c
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 8 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,29 @@ Clamping involves clearing the lowest 3 bits of the result, ensuring the result
crypto_secretstream_xchacha20poly1305_TAG_FINAL
```
#### `crypto_box`
[Sealed box encryption](https://sodium-friends.github.io/docs/docs/sealedboxencryption)
#### Methods
```
crypto_box_keypair
crypto_box_seal
crypto_box_seal_open
```
#### Constants
```
crypto_box_SEALBYTES
crypto_box_PUBLICKEYBYTES
crypto_box_SECRETKEYBYTES
crypto_box_SEEDBYTES
crypto_box_NONCEBYTES
crypto_box_MACBYTES
```
#### `crypto_secretbox`
[Secret key box encryption](https://sodium-friends.github.io/docs/docs/secretkeyboxencryption)
Expand Down
102 changes: 102 additions & 0 deletions android/src/main/cpp/sodium-jni.c
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,108 @@ Java_com_sodiumreactnative_jni_SodiumReactNativeJNI_crypto_1stream_1noncebytes(
return (jint)crypto_stream_NONCEBYTES;
}

JNIEXPORT jint JNICALL
Java_com_sodiumreactnative_jni_SodiumReactNativeJNI_crypto_1box_1sealbytes(
JNIEnv *env,
jclass clazz
) {
return (jint)crypto_box_SEALBYTES;
}

JNIEXPORT jint JNICALL
Java_com_sodiumreactnative_jni_SodiumReactNativeJNI_crypto_1box_1publickeybytes(
JNIEnv *env,
jclass clazz
) {
return (jint)crypto_box_PUBLICKEYBYTES;
}

JNIEXPORT jint JNICALL
Java_com_sodiumreactnative_jni_SodiumReactNativeJNI_crypto_1box_1secretkeybytes(
JNIEnv *env,
jclass clazz
) {
return (jint)crypto_box_SECRETKEYBYTES;
}

JNIEXPORT jint JNICALL
Java_com_sodiumreactnative_jni_SodiumReactNativeJNI_crypto_1box_1seedbytes(
JNIEnv *env,
jclass clazz
) {
return (jint)crypto_box_SEEDBYTES;
}

JNIEXPORT jint JNICALL
Java_com_sodiumreactnative_jni_SodiumReactNativeJNI_crypto_1box_1noncebytes(
JNIEnv *env,
jclass clazz
) {
return (jint)crypto_box_NONCEBYTES;
}

JNIEXPORT jint JNICALL
Java_com_sodiumreactnative_jni_SodiumReactNativeJNI_crypto_1box_1macbytes(
JNIEnv *env,
jclass clazz
) {
return (jint)crypto_box_MACBYTES;
}

JNIEXPORT jint JNICALL
Java_com_sodiumreactnative_jni_SodiumReactNativeJNI_crypto_1box_1keypair(
JNIEnv *jenv,
jclass clazz,
jbyteArray j_pk,
jbyteArray j_sk
) {
unsigned char *pk = (unsigned char *) (*jenv)->GetByteArrayElements(jenv, j_pk, 0);
unsigned char *sk = (unsigned char *) (*jenv)->GetByteArrayElements(jenv, j_sk, 0);

int result = crypto_box_keypair(pk, sk);
(*jenv)->ReleaseByteArrayElements(jenv, j_pk, (jbyte *) pk, 0);
(*jenv)->ReleaseByteArrayElements(jenv, j_sk, (jbyte *) sk, 0);
return (jint)result;
}

JNIEXPORT jint JNICALL
Java_com_sodiumreactnative_jni_SodiumReactNativeJNI_crypto_1box_1seal(
JNIEnv *jenv,
jclass clazz,
jbyteArray j_c,
jbyteArray j_m,
jint j_mlen,
jbyteArray j_pk
) {
unsigned char *c = (unsigned char *) (*jenv)->GetByteArrayElements(jenv, j_c, 0);
unsigned char *m = as_unsigned_char_array(jenv, j_m);
unsigned char *pk = as_unsigned_char_array(jenv, j_pk);

int result = crypto_box_seal(c, m, j_mlen, pk);
(*jenv)->ReleaseByteArrayElements(jenv, j_c, (jbyte *) c, 0);
return (jint)result;
}

JNIEXPORT jint JNICALL
Java_com_sodiumreactnative_jni_SodiumReactNativeJNI_crypto_1box_1seal_1open(
JNIEnv *jenv,
jclass clazz,
jbyteArray j_m,
jbyteArray j_c,
jint j_clen,
jbyteArray j_pk,
jbyteArray j_sk
) {
unsigned char *m = (unsigned char *) (*jenv)->GetByteArrayElements(jenv, j_m, 0);
unsigned char *c = as_unsigned_char_array(jenv, j_c);
unsigned char *pk = as_unsigned_char_array(jenv, j_pk);
unsigned char *sk = as_unsigned_char_array(jenv, j_sk);

int result = crypto_box_seal_open(m, c, j_clen, pk, sk);
(*jenv)->ReleaseByteArrayElements(jenv, j_m, (jbyte *) m, 0);
return (jint)result;
}

/* *****************************************************************************
* Key exchange
* *****************************************************************************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,22 +119,22 @@ public Map<String, Object> getConstants() {
constants.put("crypto_sign_SECRETKEYBYTES", SodiumReactNative.crypto_sign_secretkeybytes());
constants.put("crypto_stream_KEYBYTES", SodiumReactNative.crypto_stream_keybytes());
constants.put("crypto_stream_NONCEBYTES", SodiumReactNative.crypto_stream_noncebytes());
constants.put("crypto_box_SEALBYTES", SodiumReactNative.crypto_box_sealbytes());
constants.put("crypto_box_SEEDBYTES", SodiumReactNative.crypto_box_seedbytes());
constants.put("crypto_box_PUBLICKEYBYTES", SodiumReactNative.crypto_box_publickeybytes());
constants.put("crypto_box_SECRETKEYBYTES", SodiumReactNative.crypto_box_secretkeybytes());
constants.put("crypto_box_NONCEBYTES", SodiumReactNative.crypto_box_noncebytes());
constants.put("crypto_box_MACBYTES", SodiumReactNative.crypto_box_macbytes());

// These may be useful for future extensions

// constants.put("crypto_auth_BYTES", SodiumReactNative.crypto_auth_bytes());
// constants.put("crypto_auth_KEYBYTES", SodiumReactNative.crypto_auth_keybytes());
// constants.put("crypto_hash_BYTES", SodiumReactNative.crypto_hash_bytes());
// constants.put("crypto_box_SEEDBYTES", SodiumReactNative.crypto_box_seedbytes());
// constants.put("crypto_box_PUBLICKEYBYTES", SodiumReactNative.crypto_box_publickeybytes());
// constants.put("crypto_box_SECRETKEYBYTES", SodiumReactNative.crypto_box_secretkeybytes());
// constants.put("crypto_box_NONCEBYTES", SodiumReactNative.crypto_box_noncebytes());
// constants.put("crypto_box_MACBYTES", SodiumReactNative.crypto_box_macbytes());
// constants.put("crypto_hash_sha256_STATEBYTES", SodiumReactNative.crypto_hash_sha256_statebytes());
// constants.put("crypto_hash_sha512_STATEBYTES", SodiumReactNative.crypto_hash_sha512_statebytes());
// constants.put("crypto_stream_xor_STATEBYTES", SodiumReactNative.crypto_stream_xor_statebytes());
// constants.put("crypto_stream_chacha20_xor_STATEBYTES", SodiumReactNative.crypto_stream_chacha20_xor_statebytes());
// constants.put("crypto_box_SEALBYTES", SodiumReactNative.crypto_box_sealbytes());
// constants.put("crypto_hash_sha256_BYTES", SodiumReactNative.crypto_hash_sha256_bytes());
// constants.put("crypto_pwhash_scryptsalsa208sha256_BYTES_MIN", SodiumReactNative.crypto_pwhash_scryptsalsa208sha256_bytes_min());
// constants.put("crypto_pwhash_scryptsalsa208sha256_BYTES_MAX", SodiumReactNative.crypto_pwhash_scryptsalsa208sha256_bytes_max());
Expand Down Expand Up @@ -1211,4 +1211,94 @@ public WritableArray sodium_unpad (ReadableArray buf, int padded_buflen, int blo

return ArrayUtil.toWritableArray( Arrays.copyOfRange(_buf, 0, unpadded_buflen[0] ) );
}

@ReactMethod(isBlockingSynchronousMethod = true)
public WritableArray crypto_box_keypair (
ReadableArray pk,
ReadableArray sk
) throws Exception {
byte[] _pk = ArgumentsEx.toByteArray(pk);
byte[] _sk = ArgumentsEx.toByteArray(sk);

try {
ArgumentsEx.check(_pk, SodiumReactNative.crypto_box_publickeybytes(), "ERR_BAD_KEY");
ArgumentsEx.check(_sk, SodiumReactNative.crypto_box_secretkeybytes(), "ERR_BAD_KEY");
} catch (Exception e) {
throw e;
}

int success = SodiumReactNative.crypto_box_keypair(_pk, _sk);
if (success != 0) {
Exception e = new Exception("crypto_box_keypair execution failed");
throw e;
}

ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );

try {
outputStream.write( _pk );
outputStream.write( _sk );
} catch (IOException e) {
throw e;
}

byte ret[] = outputStream.toByteArray( );

return ArrayUtil.toWritableArray(ret);
}

@ReactMethod(isBlockingSynchronousMethod = true)
public WritableArray crypto_box_seal (
ReadableArray c,
ReadableArray m,
ReadableArray pk
) throws Exception {
byte[] _c = ArgumentsEx.toByteArray(c);
byte[] _m = ArgumentsEx.toByteArray(m);
byte[] _pk = ArgumentsEx.toByteArray(pk);

try {
ArgumentsEx.check(_pk, SodiumReactNative.crypto_box_publickeybytes(), "ERR_BAD_KEY");
ArgumentsEx.check(_c, _m.length + SodiumReactNative.crypto_box_sealbytes(), "ERR_BAD_CIPHERTEXT_LENGTH");
} catch (Exception e) {
throw e;
}

int success = SodiumReactNative.crypto_box_seal(_c, _m, _m.length, _pk);
if (success != 0) {
Exception e = new Exception("crypto_box_seal execution failed");
throw e;
}

return ArrayUtil.toWritableArray(_c);
}

@ReactMethod(isBlockingSynchronousMethod = true)
public WritableArray crypto_box_seal_open (
ReadableArray m,
ReadableArray c,
ReadableArray pk,
ReadableArray sk
) throws Exception {
byte[] _m = ArgumentsEx.toByteArray(m);
byte[] _c = ArgumentsEx.toByteArray(c);
byte[] _pk = ArgumentsEx.toByteArray(pk);
byte[] _sk = ArgumentsEx.toByteArray(sk);

try {
ArgumentsEx.check(_pk, SodiumReactNative.crypto_box_publickeybytes(), "ERR_BAD_KEY");
ArgumentsEx.check(_sk, SodiumReactNative.crypto_box_secretkeybytes(), "ERR_BAD_KEY");
ArgumentsEx.check(_m, _c.length - SodiumReactNative.crypto_box_sealbytes(), "ERR_BAD_PLAINTEXT_LENGTH");
} catch (Exception e) {
throw e;
}

int success = SodiumReactNative.crypto_box_seal_open(_m, _c, _c.length, _pk, _sk);
if (success != 0) {
Exception e = new Exception("crypto_box_seal_open execution failed");
throw e;
}

return ArrayUtil.toWritableArray(_m);
}
}
36 changes: 36 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,37 @@ export default function App() {
setResult(decoded);
};

const onCryptoBoxSeal = async () => {
const message = b4a.from('Hello, World!');
const cipherText = new Uint8Array(
message.byteLength + sodium.crypto_box_SEALBYTES
);
const pk = new Uint8Array(sodium.crypto_box_PUBLICKEYBYTES);
const sk = new Uint8Array(sodium.crypto_box_SECRETKEYBYTES);

try {
sodium.crypto_box_keypair(pk, sk);
console.log({ pk: b4a.toString(pk, 'hex') });
console.log({ sk: b4a.toString(sk, 'hex') });
sodium.crypto_box_seal(cipherText, message, pk);
const encrypted = b4a.toString(cipherText, 'hex');
console.log({ encrypted });
setResult(`Encrypted: ${encrypted}\nDecrypting in 2 seconds...`);
await sleep(2000);

const decrypted = new Uint8Array(
cipherText.byteLength - sodium.crypto_box_SEALBYTES
);
sodium.crypto_box_seal_open(decrypted, cipherText, pk, sk);
const decoded = b4a.toString(decrypted);
console.log({ decoded });
setResult(`Decrypted: ${decoded}`);
} catch (error: any) {
console.log(error);
setResult(error.message);
}
};

const openTests = () => {
setShowTests(true);
};
Expand Down Expand Up @@ -338,6 +369,11 @@ export default function App() {
title="RandomBytesBuf"
onPress={onRandomBytesBuf}
/>
<Button
style={styles.button}
title="CryptoBoxSeal"
onPress={onCryptoBoxSeal}
/>
<Button style={styles.button} title="Open tests" onPress={openTests} />
</View>

Expand Down
46 changes: 46 additions & 0 deletions ios/SodiumReactNative.mm
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ - (NSDictionary *)constantsToExport
@"crypto_sign_SEEDBYTES": @ crypto_sign_SEEDBYTES,
@"crypto_stream_KEYBYTES": @ crypto_stream_KEYBYTES,
@"crypto_stream_NONCEBYTES": @ crypto_stream_NONCEBYTES,
@"crypto_box_SEALBYTES": @ crypto_box_SEALBYTES,
@"crypto_box_PUBLICKEYBYTES": @ crypto_box_PUBLICKEYBYTES,
@"crypto_box_SECRETKEYBYTES": @ crypto_box_SECRETKEYBYTES,
@"crypto_box_SEEDBYTES": @ crypto_box_SEEDBYTES,
@"crypto_box_NONCEBYTES": @ crypto_box_NONCEBYTES,
@"crypto_box_MACBYTES": @ crypto_box_MACBYTES,
};
}

Expand Down Expand Up @@ -896,4 +902,44 @@ + (BOOL)requiresMainQueueSetup
RN_RETURN_BUFFERS_2(pk, sk, sklen)
}

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(
crypto_box_keypair: (NSArray *) pk sk: (NSArray *) sk)
{
RN_RESULT_BUFFER(pk, crypto_box_PUBLICKEYBYTES, ERR_BAD_KEY)
RN_RESULT_BUFFER(sk, crypto_box_SECRETKEYBYTES, ERR_BAD_KEY)

crypto_box_keypair(pk_data, sk_data);

RN_RETURN_BUFFERS_2(pk, sk, sklen)
}

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(
crypto_box_seal:(NSArray*)c m:(NSArray*)m pk:(NSArray*)pk)
{
RN_ARG_BUFFER_NO_CHECK(pk)
RN_ARG_BUFFER_NO_CHECK(m)

unsigned long long clen_check = mlen + crypto_box_SEALBYTES;
RN_RESULT_BUFFER(c, clen_check, ERR_BAD_CIPHERTEXT)

RN_CHECK_FAILURE(crypto_box_seal(c_data, m_data, mlen, pk_data))

RN_RETURN_BUFFER(c)
}

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(
crypto_box_seal_open:(NSArray*)m c:(NSArray*)c pk:(NSArray*)pk sk:(NSArray*)sk)
{
RN_ARG_BUFFER_NO_CHECK(pk)
RN_ARG_BUFFER_NO_CHECK(sk)
RN_ARG_BUFFER_NO_CHECK(c)

unsigned long long mlen_check = clen - crypto_box_SEALBYTES;
RN_RESULT_BUFFER(m, mlen_check, ERR_BAD_MESSAGE_LENGTH)

RN_CHECK_FAILURE(crypto_box_seal_open(m_data, c_data, clen, pk_data, sk_data))

RN_RETURN_BUFFER(m)
}

@end
Loading

0 comments on commit e934d3c

Please sign in to comment.