I'm storing AES decryption key in AndroidKeyStore to decrypt data from SQLiteDB. Unfortunately, sometimes I get this error (Any android device and any API) rarely.
I would like to know what exactly this error means.
javax.crypto.BadPaddingException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT.
Where I can find list of these error:*?
Decryption seems to work differently in Android from default Java Code in backend. In my case I was getting this error cause. Re-encoding the value of IV and SecretKey as UTF-8.
I was converting the KEY and IV like below and I was causing string to re-encoded.
val ivParameterSpec = IvParameterSpec(iv.toByteArray(charset("utf-8")))
val secretKeySpec = SecretKeySpec(key.toByteArray(charset("utf-8")), "AES")
I removed the "UTF-8" part cause it's in default converted as "UTF-8" no need to pass it again.
val ivParameterSpec = IvParameterSpec(iv.toByteArray())
val secretKeySpec = SecretKeySpec(key.toByteArray(), "AES")
(Note: Length of encryptedData is 128. IV and Key lengths are 16. Which are all divisible by 16 as algorithm needed.)
Full-code is below.
fun decrypt(encryptedData: String, iv: String, key: String): String? {
try {
//IV & Key Generation
val ivParameterSpec = IvParameterSpec(iv.toByteArray())
val secretKeySpec = SecretKeySpec(key.toByteArray(), "AES")
val c = Cipher.getInstance("AES/CBC/PKCS5PADDING")
c.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
val decodedValue = Base64.decode(encryptedData, Base64.URL_SAFE)
val decValue = c.doFinal(decodedValue)
return String(decValue)
} catch (ex: IllegalBlockSizeException) {
println(ex.message)
} catch (ex: BadPaddingException) {
println(ex.message)
} catch (ex: InvalidKeyException) {
println(ex.message)
} catch (ex: NoSuchAlgorithmException) {
println(ex.message)
} catch (ex: NoSuchPaddingException) {
println(ex.message)
} catch (ex: Exception) {
println(ex.message)
}
return null
}
Related
We have a requirement where we will get modulus from service call to be used for encrypting (JWE) some data at client side
Example of modulus, and exponent (cannot be changed formats)
{
"modulus": "27921539673885756361419156461021876226845445107119091471889357242434620975884246915491757578902818243635599516623468500514580405147004224500529164165175418080296534603814824585285975894063860621755306618118216921360279638839263914961253991832884140339596254323951288609833783395465326689135784495563016459486626252473786100255392919521063720839389818627863002071132030078117996952009416186398741189513852165509082498538217432301912889497439381201450665516807813007876250433333575497298297093128243716605741969339467610040987340399020751603712326128652340270376013354086995479489210853404012768884141354633100389617819",
"exponent": "65537",
}
I am trying to use JavaScript Jose library to generate JWE, but no luck.
I am following below example from internet, but my question is modulus I am using seems to be in biginteger (i guess) format, but the example showing different format.
Is there a better way I can use JOSE for JWE if I have modulus and exponent as shown above? Or can it be done as its shown below, but I am getting an error:
var cryptographer = new Jose.WebCryptographer();
var rsa_key = Jose.Utils.importRsaPublicKey(
{
n:
"c2:4b:af:0f:2d:2b:ad:36:72:a7:91:0f:ee:30:a0:95:d5:3a:46:82:86:96:7e:42:c6:fe:8f:20:97:af:49:f6:48:a3:91:53:ac:2e:e6:ec:9a:9a:e0:0a:fb:1c:db:44:40:5b:8c:fc:d5:1c:cb:b6:9b:60:c0:a8:ac:06:f1:6b:29:5e:2f:7b:09:d9:93:32:da:3f:db:53:9c:2e:ea:3b:41:7f:6b:c9:7b:88:9f:2e:c5:dd:42:1e:7f:8f:04:f6:60:3c:fe:43:6d:32:10:ce:8d:99:cb:76:f7:10:97:05:af:28:1e:39:0f:78:35:50:7b:8e:28:22:a4:7d:11:51:22:d1:0e:ab:6b:6f:96:cb:cf:7d:eb:c6:aa:a2:6a:2e:97:2a:93:af:a5:89:e6:c8:bc:9f:fd:85:2b:0f:b4:c0:e4:ca:b5:a7:9a:01:05:81:93:6b:f5:8d:1c:f7:f3:77:0e:6e:53:34:92:0f:48:21:34:33:44:14:5e:4a:00:41:3a:7d:cb:38:82:c1:65:e0:79:ea:a1:05:84:b2:6e:40:19:77:1a:0e:38:4b:28:1f:34:b5:cb:ac:c5:2f:58:51:d7:ec:a8:08:0e:7c:c0:20:c1:5e:a1:4d:b1:30:17:63:0e:e7:58:8e:7f:6e:9f:a4:77:8b:1e:a2:d2:2e:1b:e9",
e: 65537
},
"RSA-OAEP"
);
var encrypter = new Jose.JoseJWE.Encrypter(cryptographer, rsa_key);
encrypter
.encrypt("hello world")
.then(function(result) {
console.log(result);
})
.catch(function(err) {
console.error(err);
});
The decimal string representing the modulus must be converted to a hexadecimal string with the proper formatting. In the following code this is done by the convert function.
The example encrypts and decrypts a plaintext and is essentially taken from the description of js-jose. The converted modulus is used for the encryption. The validity of the conversion can be cross checked e.g. here (and can of course also be concluded from the successful decryption):
function convert(modulus){
var nHex = BigInt(modulus).toString(16); // Convert from dec to hex
var nHexPadded = nHex.length % 2 ? "0" + nHex : nHex; // Pad if necessary with a leading 0
var nHexPaddedFormatted = nHexPadded.match(/.{1,2}/g).join(':'); // Separate each byte with a :
return nHexPaddedFormatted;
}
var exponent = "65537";
var modulus = "24527552248658638593238312534622665239452237617916915035327410973877933321625015819725141075075211032977064656616272935088402961419107720169032602127557052115192166251022433499097931189631750439635561603835889998309453723345666313950232348492443075827282756304618422730925476259619200114387693134398859680038905017069367126679713008966792133309640987499113594495057801427098321936394744165577110835460409347737585308896989425915113198172049505101019813693205226199749749144689080410566381237890461144281136847243342701604504885554436502186579457750948522590884398208445448252725393629705457022749423572745479491820521";
console.log(convert(modulus).replace(/(.{60})/g,'$1\n'));
var publicKey = Jose.Utils.importRsaPublicKey(
{
/*
n: "c2:4b:af:0f:2d:2b:ad:36:72:a7:91:0f:ee:30:a0:95:d5:3a:46:82:86:96:7e:42:c6:fe:8f:20:97:af:49:f6:48:a3:91:53:ac:2e:e6:ec:9a:9a:e0:0a:fb:1c:db:44:40:5b:8c:fc:d5:1c:cb:b6:9b:60:c0:a8:ac:06:f1:6b:29:5e:2f:7b:09:d9:93:32:da:3f:db:53:9c:2e:ea:3b:41:7f:6b:c9:7b:88:9f:2e:c5:dd:42:1e:7f:8f:04:f6:60:3c:fe:43:6d:32:10:ce:8d:99:cb:76:f7:10:97:05:af:28:1e:39:0f:78:35:50:7b:8e:28:22:a4:7d:11:51:22:d1:0e:ab:6b:6f:96:cb:cf:7d:eb:c6:aa:a2:6a:2e:97:2a:93:af:a5:89:e6:c8:bc:9f:fd:85:2b:0f:b4:c0:e4:ca:b5:a7:9a:01:05:81:93:6b:f5:8d:1c:f7:f3:77:0e:6e:53:34:92:0f:48:21:34:33:44:14:5e:4a:00:41:3a:7d:cb:38:82:c1:65:e0:79:ea:a1:05:84:b2:6e:40:19:77:1a:0e:38:4b:28:1f:34:b5:cb:ac:c5:2f:58:51:d7:ec:a8:08:0e:7c:c0:20:c1:5e:a1:4d:b1:30:17:63:0e:e7:58:8e:7f:6e:9f:a4:77:8b:1e:a2:d2:2e:1b:e9",
*/
n: convert(modulus), // Apply the converted modulus
e: parseInt(exponent)
},
"RSA-OAEP"
);
var privateKey = Jose.Utils.importRsaPrivateKey(
{
n:
"00:c2:4b:af:0f:2d:2b:ad:36:72:a7:91:0f:ee:30:a0:95:d5:3a:46:82:86:96:7e:42:c6:fe:8f:20:97:af:49:f6:48:a3:91:53:ac:2e:e6:ec:9a:9a:e0:0a:fb:1c:db:44:40:5b:8c:fc:d5:1c:cb:b6:9b:60:c0:a8:ac:06:f1:6b:29:5e:2f:7b:09:d9:93:32:da:3f:db:53:9c:2e:ea:3b:41:7f:6b:c9:7b:88:9f:2e:c5:dd:42:1e:7f:8f:04:f6:60:3c:fe:43:6d:32:10:ce:8d:99:cb:76:f7:10:97:05:af:28:1e:39:0f:78:35:50:7b:8e:28:22:a4:7d:11:51:22:d1:0e:ab:6b:6f:96:cb:cf:7d:eb:c6:aa:a2:6a:2e:97:2a:93:af:a5:89:e6:c8:bc:9f:fd:85:2b:0f:b4:c0:e4:ca:b5:a7:9a:01:05:81:93:6b:f5:8d:1c:f7:f3:77:0e:6e:53:34:92:0f:48:21:34:33:44:14:5e:4a:00:41:3a:7d:cb:38:82:c1:65:e0:79:ea:a1:05:84:b2:6e:40:19:77:1a:0e:38:4b:28:1f:34:b5:cb:ac:c5:2f:58:51:d7:ec:a8:08:0e:7c:c0:20:c1:5e:a1:4d:b1:30:17:63:0e:e7:58:8e:7f:6e:9f:a4:77:8b:1e:a2:d2:2e:1b:e9",
e: 65537,
d:
"37:b6:4b:f4:26:17:a8:0b:3c:c5:1f:ab:59:b9:47:d2:ae:d9:8e:ee:4e:79:48:ab:0d:34:61:06:0f:78:8b:d4:ba:ef:6b:f4:7a:22:d8:c4:6f:70:89:5d:9c:b3:a1:8b:e8:88:57:dd:07:9e:c2:2b:12:52:a3:eb:b9:a8:24:01:7e:53:2b:7a:34:50:d7:0c:75:d8:69:a3:87:dd:4b:fc:c1:c3:2f:bd:0e:57:16:8d:ea:de:8e:de:ff:e4:9a:9f:aa:e8:d2:5f:b3:27:ef:f9:ca:50:97:2e:fd:99:1c:34:dd:0c:bb:dd:d0:b9:bf:4f:dc:9d:de:94:50:66:2c:58:7e:c2:31:8b:41:56:49:6a:e6:11:14:53:a1:45:0d:15:8b:26:79:0f:c9:dc:ac:dc:c7:bc:55:2c:96:ed:a7:29:09:04:ee:00:74:60:e1:bc:97:7b:0a:b6:f2:83:82:79:65:e0:aa:88:9f:90:b0:0d:76:4d:3c:08:7e:a5:05:19:d4:8b:54:d3:f1:c1:a3:e3:a5:1e:aa:d6:c4:94:ad:6c:b3:8f:85:06:8a:6f:52:f8:a3:c3:e0:8d:67:35:2f:d4:18:fc:70:f4:71:bf:18:88:d6:a7:b7:04:8e:d3:06:ca:83:c3:2d:21:98:65:c9:41:2c:77:bf:4c:7c:8c:2c:01",
p:
"00:fa:d6:06:46:5c:04:70:e6:ec:47:02:96:02:a5:e2:41:9d:bd:7b:97:28:a4:c5:3b:b5:9b:0a:6b:7d:b6:44:8a:28:1e:d1:ef:cb:44:ef:eb:4d:08:74:80:f5:cf:3b:b7:40:10:60:c9:18:1e:a5:76:4b:41:37:06:b2:71:03:60:25:77:db:d0:b2:21:dc:b0:32:90:a2:10:9a:d5:e6:e3:11:42:a1:9a:7a:26:3c:d3:12:56:db:25:07:69:be:ae:2c:b9:33:6c:29:e3:65:b9:5b:05:84:05:e6:da:c4:f4:3f:ab:84:60:6e:f0:5f:ba:a8:98:8f:72:2c:c8:40:d1",
q:
"00:c6:4b:ac:fe:40:1c:dc:6c:78:07:cc:3e:db:4e:d5:d0:17:3b:8f:04:f0:ae:c4:22:0d:8b:0a:4d:0f:9e:fe:c7:e6:38:b5:53:ba:a9:e8:f0:47:28:14:25:95:6a:79:ab:db:86:97:82:c5:1e:bd:80:a5:aa:a2:b7:a5:c7:48:17:c4:d9:c7:4f:50:2a:69:67:15:4c:0b:f5:e6:fb:20:23:5d:ea:ae:6c:c6:74:ba:cc:f8:06:2b:41:1f:b6:3f:2a:93:fa:f9:e1:ee:93:c3:92:ad:49:c7:8f:db:72:ff:6b:f0:f0:d6:2f:83:ce:1c:82:16:89:57:01:9f:49:2f:99",
dp:
"57:d4:c1:75:b9:9a:c4:7d:d7:96:35:cd:99:37:c4:b5:fd:29:f0:30:c9:c6:88:59:94:09:a9:e8:61:a8:84:ef:6b:84:ff:35:dc:13:53:7f:2d:06:1c:e5:5b:2d:29:57:cd:52:ee:d0:fb:65:1f:c3:00:2e:e1:b9:b2:99:e7:f8:ae:a5:fd:8e:62:11:81:59:21:1b:8b:e4:0c:93:81:b9:58:bd:e0:20:5b:4d:30:57:28:40:c9:93:79:b9:09:4f:ab:d1:5d:b4:2e:26:b5:e3:e5:7f:54:ef:4c:1a:a6:84:70:16:fa:cf:59:89:49:bb:ee:75:1d:25:79:90:d5:41",
dq:
"00:ab:eb:a8:8c:b7:21:4e:aa:6c:56:b6:6a:38:d1:dc:e6:91:7d:fd:bd:96:be:af:25:a7:00:49:6a:0e:85:16:f8:51:4e:11:48:0a:aa:8d:5e:e5:12:86:85:1f:4a:35:3b:1f:15:4d:fe:fe:d0:6c:14:41:8d:f3:8d:ad:99:5d:93:de:03:c2:9d:ad:2f:58:3b:1b:67:d7:66:d7:60:1a:b9:0f:10:0d:32:19:cd:d2:b7:2a:c2:8e:75:e3:fc:aa:3f:4c:15:68:d8:cd:74:27:37:e0:2d:fb:6b:6a:24:05:f7:9b:e9:f2:89:37:89:57:86:21:eb:e9:17:6a:f6:94:e1",
qi:
"0a:ed:5f:30:67:d5:e5:6e:4a:7a:35:49:fe:16:2f:1e:91:2b:39:c3:01:d3:d4:c0:4d:b3:fc:08:b0:66:e9:44:10:9e:5b:5a:ea:83:a5:9c:95:7a:58:70:35:28:e5:4d:ba:19:de:0d:66:f9:db:5c:f6:5b:24:27:9d:0b:2d:44:40:eb:33:3a:19:e2:1d:c0:b0:16:99:d1:c1:52:84:02:d6:67:06:32:f8:4d:cb:42:9f:7c:8a:e0:ad:df:40:6f:e4:8c:f6:f6:9e:1d:bd:43:e3:38:91:a2:d0:9e:60:ff:9d:8c:fb:72:5b:df:95:30:17:d2:f2:cb:7d:92:56:0a"
},
"RSA-OAEP"
);
// Example based on https://github.com/square/js-jose#example-encryption
var cryptographer = new Jose.WebCryptographer();
var encrypter = new Jose.JoseJWE.Encrypter(cryptographer, publicKey);
encrypter
.encrypt("The quick brown fox jumps over the lazy dog")
.then(function(jwe) {
console.log(jwe.replace(/(.{60})/g,'$1\n'));
var decrypter = new Jose.JoseJWE.Decrypter(cryptographer, privateKey);
decrypter
.decrypt(jwe)
.then(function(jwe_plain_text) {
console.log(jwe_plain_text.replace(/(.{60})/g,'$1\n'));
});
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-jose/0.2.2/jose.min.js"></script>
I am using cryptoSwift library for encryption and decryption. But it's working with only string 16 bytes. If i am passing small string or less than 16 bytes then getting nil result.
I am using Incremental operations use instance of Cryptor and encrypt/decrypt one part at a time.
Please help me here, is there anything which i am doing wrong.
Thanks in advance.
func encAndDec(){
do {
// Encryption start
let data = Data.init(base64Encoded: "12345678901234567890123456789012".base64Encoded()!)
let iv : Array<UInt8> = [0,0,0,0,0,0,0,0,0,0,0,0]
let nIv = Data(iv)
let gcmEnc = GCM(iv: nIv.bytes, mode: .detached)
var enc = try? AES(key: data!.bytes, blockMode: gcmEnc, padding: .noPadding).makeEncryptor()
let arrStr = ["My name is tarun"] // Working
//let arrStr = ["tarun"] // Not working for this string
var ciphertext = Array<UInt8>()
for txt in arrStr{
let ciperText = try? enc?.update(withBytes: Array(txt.utf8)) // Getting nil for small string.
ciphertext.append(contentsOf: ciperText!)
}
var res = try? enc?.finish()
gcmEnc.authenticationTag = self.randomGenerateBytes(count: 16)?.bytes
res?.append(contentsOf: (gcmEnc.authenticationTag)!)
let cipherData = Data(ciphertext) + Data(res!)
let strEnc = String(decoding: cipherData, as: UTF8.self)
print(strEnc)
// Decryption start from here
do {
let gcmDec = GCM.init(iv: nIv.bytes, additionalAuthenticatedData: nil, tagLength: 16, mode: .detached)
var aesDec = try! AES(key: data!.bytes, blockMode: gcmDec, padding: .noPadding).makeDecryptor()
let tag_length = 16
let encData = cipherData.subdata(in: 0..<cipherData.count - tag_length)
let tag = cipherData.subdata(in: encData.count ..< cipherData.count)
let decData = try? aesDec.update(withBytes: encData.bytes) //Getting nil here for small string
let strData = String(decoding: decData!, as: UTF8.self)
print(strData)
do{
var res = try? aesDec.finish(withBytes: tag.bytes)
res?.append(contentsOf: tag)
}catch{
}
} catch {
// failed
}
}
}
func randomGenerateBytes(count: Int) -> Data? {
let bytes = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: 1)
defer { bytes.deallocate() }
let status = CCRandomGenerateBytes(bytes, count)
guard status == kCCSuccess else { return nil }
return Data(bytes: bytes, count: count)
}
There's nothing wrong with aes-256-gcm implementation in CryptoSwift, as some of the commenters have suggested, you just have some bugs in your code. Hopefully the following will help you out.
I'm just going to call it GCM below for brevity.
GCM encryption takes as input the plaintext, a key, and an initialization vector and produces ciphertext and an authentication tag. In your code, you set the authentication tag to random bytes, overwriting the authentication tag.
I think it's a bit clearer if you break your code up into some functions, each with a clearly defined purpose. I also stripped away some of the conversions from Data to and from [UInt8] for clarity.
Here's what the encryption function would look like:
func enc(plainText: [String], key: [UInt8], iv: [UInt8]) throws -> (cipherText: [UInt8], authenticationTag: [UInt8]?)
{
let gcmEnc = GCM(iv: iv, mode: .detached)
var enc = try AES(key: key, blockMode: gcmEnc, padding: .noPadding).makeEncryptor()
var ciphertext = Array<UInt8>()
for txt in plainText {
ciphertext += try enc.update(withBytes: Array(txt.utf8))
}
ciphertext += try enc.finish()
return (ciphertext, gcmEnc.authenticationTag)
}
When you're decrypting GCM you need to pass in the ciphertext, key, initialization vector and the authentication tag. That would look like this:
func dec(cipherText: [UInt8], authenticationTag: [UInt8]?, key: [UInt8], iv: [UInt8]) throws -> [UInt8]? {
let tagLength = authenticationTag?.count ?? 0
let gcmDec = GCM.init(iv: iv, additionalAuthenticatedData: nil, tagLength: tagLength, mode: .detached)
gcmDec.authenticationTag = authenticationTag
var aesDec = try AES(key: key, blockMode: gcmDec, padding: .noPadding).makeDecryptor()
var decData = try aesDec.update(withBytes: cipherText)
decData += try aesDec.finish()
return decData
}
In both cases, you need to make sure that you append the output of the finish call to the ciphertext or plaintext. This is particularly important with small amounts of data as the update method may produce nothing!
With these two functions written you can rewrite your test function as follows:
func encAndDec(){
do {
guard let key = Data.init(base64Encoded: "12345678901234567890123456789012".base64Encoded()!)
else {
fatalError("Failed to create key")
}
let iv : Array<UInt8> = [0,0,0,0,
0,0,0,0,
0,0,0,0]
//let arrStr = ["My name is tarun"] // Working
let arrStr = ["tarun"] // Not working for this string
let (cipherText, authenticationTag) = try enc(plainText: arrStr, key: key.bytes, iv: iv)
guard let decrypedPlainText = try dec(cipherText: cipherText,
authenticationTag: authenticationTag, key: key.bytes, iv: iv) else {
fatalError("Decryption return nil")
}
guard let decryptedString = String(bytes: decrypedPlainText, encoding: .utf8) else {
fatalError("Failed to convert plaintext to string using UTF8 encoding.")
}
print("Decrypted Plaintext: \(decryptedString)")
}
catch let e {
print("EXCEPTION: \(e)")
}
}
If you run this you'll find it produces the expected output.
The complete example code can be found at: https://gist.github.com/iosdevzone/45456d2911bf2422bc4a6898876ba0ab
I don't believe GCM requires PADDING. Here is an example pretty much straight from the NODE.JS documentation that works fine and does not use padding. The line below will show the Ciphertext length is 5
I have done the same with Ruby, Java, Go, and others and none require padding or the input value to be a multiple of 16 bytes like the Swift library seems to require. Anyone else help confirm this is a bug in Swift implementation of GCM?
const crypto = require('crypto');
const key = '12345678901234567890123456789012';
const iv = '000000000000'
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
const plaintext = 'Hello';
const ciphertext = cipher.update(plaintext, 'utf8');
**console.log("ciphertext length %d", ciphertext.length)**
cipher.final();
const tag = cipher.getAuthTag();
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
const receivedPlaintext = decipher.update(ciphertext, null, 'utf8');
try {
decipher.final();
} catch (err) {
console.error('Authentication failed!');
return;
}
console.log(receivedPlaintext);
I'm just starting kotlin so I'm sure there is an easy way to do this but I don't see it. I want to split a into single-length sub strings using codepoints. In Java 8, this works:
public class UtfSplit {
static String [] utf8Split (String str) {
int [] codepoints = str.codePoints().toArray();
String [] rv = new String[codepoints.length];
for (int i = 0; i < codepoints.length; i++)
rv[i] = new String(codepoints, i, 1);
return rv;
}
public static void main(String [] args) {
String test = "こんにちは皆さん";
System.out.println("Test string:" + test);
StringBuilder sb = new StringBuilder("Result:");
for(String s : utf8Split(test))
sb.append(s).append(", ");
System.out.println(sb.toString());
}
}
Output is:
Test string:こんにちは皆さん
Result:こ, ん, に, ち, は, 皆, さ, ん,
How would I do this in kotlin?? I can get to codepoints although it's clumsy and I'm sure I'm doing it wrong. But I can't get from the codepoints back to a strings. The whole string/character interface seems different to me and I'm just not getting it.
Thanks
Steve S.
You are using the same runtime as Java so the code is basically doing the same thing. However, the Kotlin version is shorter, and also has no need for a class, although you could group utility methods into an object. Here is the version using top-level functions:
fun splitByCodePoint(str: String): Array<String> {
val codepoints = str.codePoints().toArray()
return Array(codepoints.size) { index ->
String(codepoints, index, 1)
}
}
fun main(args: Array<String>) {
val input = "こんにちは皆さん"
val result = splitByCodePoint(input)
println("Test string: ${input}")
println("Result: ${result.joinToString(", ")}")
}
Output:
Test string: こんにちは皆さん
Result: こ, ん, に, ち, は, 皆, さ, ん
Note: I renamed the function because the encoding doesn't really matter since you are just splitting by Codepoints.
Some might write this without the local variable:
fun splitByCodePoint(str: String): Array<String> {
return str.codePoints().toArray().let { codepoints ->
Array(codepoints.size) { index -> String(codepoints, index, 1) }
}
}
See also:
Kotlin stdlib Array initializer by index w/lambda
Kotlin stdlib let function
I'm not seeing anything ever get returned by the scan. I know it's because the mutableList gets returned right away, but how do I block the return until the time expires?
Basically, all I want to do is fill up the mutable list for as long as the take() permits then return that mutableList to the calling function.
This is what I have tried.
private val timeoutScheduler: Scheduler = Schedulers.computation()
fun scanForAllDevicesStartingWith(devicePrefix: String): List<String> {
Log.d(TAG, "Scanning for devices starting with $devicePrefix")
val mutableList = mutableListOf<String>()
val result = scanForDevices()
.take(3, TimeUnit.SECONDS, timeoutScheduler)
.subscribe { scanResult ->
val name = scanResult.bleDevice.name
Logger.d(TAG, "Potential device named $name found")
if(name != null) {
if(name.startsWith(prefix = devicePrefix)) {
Logger.d(TAG, "Match found $name")
mutableList.plus(name)
}
}
}
return mutableList
}
private fun scanForDevices(): Observable<ScanResult>
= rxBleClient.scanBleDevices(
ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build(),
ScanFilter.Builder()
.build())
}
OK, here it is boiled down for the next person who wants to do this kind of thing. In Rx, they have Singles which are Observables that just emit one value. In my case I needed a list of String values, so just need to use a Single of type List of type String. That gets just one element emitted that happens to be a list of Strings. The code looks like this...
fun returnAllDevicesStartingWith(devicePrefix: String): Single<List<String>> {
return scanForDevices()
.take(3, TimeUnit.SECONDS, timeoutScheduler)
.map { it.bleDevice.name }
.filter { it.startsWith(devicePrefix) }
.toList()
}
The function that calls it (written in Java instead of Kotlin) looks like this:
List<String> devices = bleUtility.returnAllDevicesStartingWith(prefix).blockingGet();
I tested it using a mocked function like this:
//Begin test code
var emittedList: List<String> = listOf("dev1-1", "dev1-2", "dev2-1", "dev2-2", "dev3-1", "dev3-2")
private fun scanForRoomDevices(): Observable<FoundDevice> = Observable
.intervalRange(0, emittedList.size.toLong(), 0, 1, TimeUnit.SECONDS, timeoutScheduler)
.map { index -> FoundDevice(emittedList[index.toInt()], BleDevice(emittedList[index.toInt()])) }
data class FoundDevice(val controllerId: String, val bleDevice: BleDevice)
data class BleDevice(val name: String)
Hope this helps others.
I'm trying to encrypt a file in Swift with the framework Cryptoswift. I made it, but with files a little bit heavy like mp4, mp3, it is very slow. I don't really know what it is happening, if I'm implementing in the wrong way or the algorithm is like that.
Here is my code.
do {
// write until all is written
let ex = "a"
func writeTo(stream: OutputStream, bytes: Array<UInt8>) {
var writtenCount = 0
while stream.hasSpaceAvailable && writtenCount < bytes.count {
writtenCount += stream.write(bytes, maxLength: bytes.count)
}
}
let path = "somewhere"
let aes = try AES(key: key, iv: iv)
var encryptor = aes.makeEncryptor()
// prepare streams
//let data = Data(bytes: (0..<100).map { $0 })
let inputStream = InputStream(fileAtPath: path)
let outputStream = OutputStream(toFileAtPath: "somewhere", append: false)
inputStream?.open()
outputStream?.open()
var buffer = Array<UInt8>(repeating: 0, count: 2)
// encrypt input stream data and write encrypted result to output stream
while (inputStream?.hasBytesAvailable)! {
let readCount = inputStream?.read(&buffer, maxLength: buffer.count)
if (readCount! > 0) {
try encryptor.update(withBytes: buffer[0..<readCount!]) { (bytes) in
writeTo(stream: outputStream!, bytes: bytes)
}
}
}
// finalize encryption
try encryptor.finish { (bytes) in
writeTo(stream: outputStream!, bytes: bytes)
}
if let ciphertext = outputStream?.property(forKey: Stream.PropertyKey(rawValue: Stream.PropertyKey.dataWrittenToMemoryStreamKey.rawValue)) as? Data {
print("Encrypted stream data: \(ciphertext.toHexString())")
}
} catch {
print(error)
}
I'd say, try to re-test with the Release build. Assuming you're looking at Debug build, it is expected that Swift code will be significantly slower