I have some source code in Kotlin that I only partially understand. I know a little bit of Python so I'm looking to recreate the decrypt function.
I have an encrypted string that I want to recover, and the original passphrase and salt too.
Here is the source (from here, with constants, encrypt function etc):
fun decrypt(passPhrase: String, salt: ByteArray, encryptedData: String): String? {
val iv: ByteArray?
val encrypted: ByteArray?
return try {
lastException = null
val byteBuffer = ByteBuffer.wrap(Base64.decode(encryptedData))
val ivLength = byteBuffer.get().toInt()
iv = ByteArray(ivLength)
byteBuffer[iv]
encrypted = ByteArray(byteBuffer.remaining())
byteBuffer[encrypted]
val cipherDec: Cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipherDec.init(Cipher.DECRYPT_MODE, prepCipherKey(passPhrase, salt), GCMParameterSpec(TAG_LENGTH_BIT, iv))
val dec = cipherDec.doFinal(encrypted)
String(dec)
} catch (e: Exception) {
lastException = e
aapsLogger.error("Decryption failed due to technical exception: $e")
null
}
}
Here is my attempt simplified (I get incorrect padding when using the inputs I have):
def generate_key(password, salt, iterations):
assert iterations > 0
key = base64.b64decode(password + salt)
for i in range(iterations):
key = hashlib.sha256(key).digest()
return key
def decrypt(ciphertext, password):
salt = 'XXXXXXXetc'
key = generate_key(password, salt, NUMBER_OF_ITERATIONS)
cipher = AES.new(key, AES.MODE_GCM)
plaintext = decrypt(ciphertext, cipher)
return plaintext
Edit:
Here is some test data, with passphrase 1234
{
"format": "aaps_encrypted",
"metadata": {
"device_name": "Galaxy S8 SE",
"created_at": "2022-04-19T19:58:17.338Z",
"aaps_version": "3.0.0.1",
"aaps_flavour": "full",
"device_model": "samsung SM-G950F (dreamlte)"
},
"security": {
"file_hash": "96cf809f37eb46d0eb837511bae539b5fadd424e74d3e6eedaa4ceb2fdac07d8",
"algorithm": "v1",
"salt": "4030cc7f15e5f7bf830543270ced2b7c8b24237c53ce95382b232df69c594353",
"content_hash": "197510b071e3e592c43301e77c997400048641e77275a02ebd41a58213e90c61"
},
"content": "DKQDMzcfTQdd59fKSN5s3pmcbs+nK4fS6VUt0+sfWDolkrUqfq33HT82GOpUwCpCtHH0zwY2plw8ZEgv1a4I\/OjDQeueAyVL8NZUiNKWPdtUwSxbfiAuP6P61H1X9iGIAvhuvR+uyy2l\/DNyWqIox2tvcNGXqt3og2mFojiqryB+tKfia8dO7bpph\/8q3\/NGtJajijqTPNCcjLid8N0yivEXKSXaceL9qdGGZNoMGmIGMhrlrS8JBKOfRvFkE7hdat6D8uaF4iyxfuLGY210oDenf2V+GcT5XBWv8nr1NKWaQTJOLx6A2QKZM\/9xKck0fl04PCFFz+d0x+Y3HgiecFrU3uCWKSL901OCUIlDdOiR7U1Ik1BJFfRzZMaB8qLU7uyc9SNzXs55u3Xm2yD14Fa44jhQKkXI\/DplqYl4M8wgSL7M2oCqNIMI4VpRpRF9apGRcdzVCjwnOWk3mBQkOUn\/etITu2kRMB8QZqvxXJtW40ZRjmqNXH7\/xWV+jDr+3QnmdvqVl7YvityxfPmTTnJdQpUYvV8SbKR2kclidhZ9pTF14yndejNrZru5IE5D7a8vuiydkL6vcAchM9ts3MBv\/lg0Bu5y8H5+DCp8lwVBZDgPnjH1CpfNtk\/nYTJnlkkmRdjozZeqjrC8J\/+saRqizgXFqblVXY2A3Z0gF9iVjbGZywsabns1vUuYalqaXKW9U8sNZvs3S0\/ngRC25Rgo6AQdloqKr4PLeCEO18jq\/Vh+Vky8MoAZXS\/xmCyFdAn+EDLCdhKEYTA9Ynl+pRTGAwtS9HLgNq36AohlKQm2vuRP1dchaelzL5tycHuNQq6zqUqh+4j0dw2vd7P8ZRvE3m5wNnOLC7lugpddxnbrwI\/j1Pu9aPlHHZWTy9dKW01IThVC9FWJqnhEhGao19FDXBEAjOUh70o2YIDb3d8oEDLMJX5Weaaoh9itMVLkafwmYx3YhqZqZ+38RykuL7x36aCksefL\/YtphPJkxwPgny2IDM6948uNUg3jUCem7ZwxHTWhgsIxI00PNo2D1ZWAOdk8HPy+gRaJ\/rXsNvo0ErW2CD4gkKT8cvwkRJ2WH+ovcVrcMFO1Ns7a8VdksjuMZhCWa8rzl6efRznT7FkXrKJSmUG5WkLQbPlSirMN2YK2R111qm7hbxvcTMFvGIh6ZZDHdDaaWMi7M3nHRT8AzlTiap1arnTCpSqtMVndB4Gu6i\/fNOFiV+3iXFfDUJsgvcGBDHsOQZ3lCdkHcCz5H1UB8D0tqqPHKt\/TAAwz\/Ew5axshAmiCuKqC+EiTTHkHKd1e5uCF5kRXJWxlzlGNWYJmVZOkPfDpV0mX81Nc9RGgAKnzZ9v\/kHvzKsCjXyPbgzXBuYB+FqaaP8CNNlyrKuTObqndUnCPIbIpBEtMf7auI2n5ME78KXd+fPX1PvCYx3z4Z6FwZVBNtfTiIQgK4obNaPG40ysdcSGonKSDEVNEUDlZOZWTdNxGfXa5XfmsYN+qw3v1+Sd2FDKjlP1BZPOmsCNEWXBrnqLsuoHnGv20che\/spEsS8aTQ4pWCerb4f7o13b303cTbSyQaC0AohH9UfjdXS19pkdHQySV2EXTLfcvfPPX+E39aZG4BiKZ86jbmUQNjhgpoFupK9VUa7ZEEwQfhUOrTvZr+zcwV0fJcD9FaaEcVLs1kez7TRhS\/p8rBUpSvoLa5KSEfquHbQq0WUr\/bSiX0hXVGY0h6MooZ8z\/HCFzzBHhHFZfRfsEbr1F9cJp7cn8MuqDuIH3GJ+6wW8JAb\/ftk\/BHMtfSvT\/TWxmldn+Z5GQwGODJ5nGQTzsFmOJszSoVubVfuEJQ94SMHAB0tULDHhIFDknDgDjEY3VuNQ51ZC8nGas4XuohLTMeZ6cmJJmTnvPYDFb0RlbVrG7TVq7VaoZNIx9tTUhmAaSvbiiDc9pHGbiWAb5oe9dpGWjPDi5kayrckDjWkImy3jJLEV+s9Dtz5R621+GzkQYw0KTKDEaqQtVkWXPP+8do2PyRn+zh5MOpV7CxVKbjs19vR2gcC2DE9Ra\/naK+cvKiwLnzmZEleBCCKsB5+9LJzHvhPrpbaFczKxDi\/A9LAwCFawGjsS6hF58tI2FpZoOw\/FacP1wb8V6sp97dCZjRH14ECMp24AuTfnmbwXmabT5\/zJUyDZoGCd7NONM0lOFBDFmKn+kwo\/gyB5Sy\/YFFVEeTEfzL+49tPfphA\/SmbeyCop51ln8BeIOTTpdAwhSNpyU2YzUB845nN6I+\/9QUO7ErkHBEhbAbwbVAiLev0dkw9J+2V4tZEMVzhoJrV5QjBiu+wvGj0pBUhmSXOUhfyqnlp3XK6FU7M63MWzRlkZarW7FFHcGdhnArqQ9st\/ETdVXGpVR3ZGPxeh0C9ycjUpPKtRxEuNf93JGf99UPxou83zoL3sfEkxfWAbDB0eW2WoNZveuPaLwN3fGEgQaxglEXlM\/L9CaihvCZocseWHtpoBS1HjdHj7gGrRF\/DPOudS8dvu\/mWmK4IaUKIErYZoBnjopDmtpzqoNENjTNkhRvp3ojxg\/DgXsos3t7RxORC07T0SC\/SjUo6veEk5COlLENb49PozZbVIL6YECXp1xJxbf\/QCFiXosA+S34ERwSwkJGYGhaPY42XJUODplYecy22FKjg2HN\/bvR213IQt9ctmZN5JB3wC8Fmyi5faxf2sl9VV3aM7ekpr3tyj7ATAvyRMl1UlnAz30ZO7GGhGhQbxYpe5SU20dpcVfC+U3VQzrsGHW5f6OaI3GJpM5PVL98bK+CfXEvC9acmPyH0AVHPWvYPEih81r2Ek4n2T6evtgrvixbRBGDTr24hB+27u29GEsZnqoL9vPx7xJeJ1WYYKYerVg+t2GiVgEmn3XOFt3CkrSrp144HrWo0ZgAipTQ8BHVJ45r5WsojRLKrazpJMYLnFBSx4iokhtODb9dv0Tum+wZ2zUIhsXDMBbAsjSDhJtrQLwuCoLzaz7o5BGinq9fIJks7uVZ5dUGe5dMJux8zNwujPVP9fuwsmmP6ObbGsG4MEhgyVoSpV39v8l7gyrulLp3D1fxbtNmZ9JolHFNtGTrkbouFjEnKn8B4iE4ofLGcuWGBnw\/zVSerFdMO8\/I4FaofLFQCLmx92s\/ROBXuycYJ4Isa4flJAwWK8SKIFpGWQg\/FfLkij611I+1i5m8wHX2XHcm5veA4ZcdcfhzqtywP8onLia0KZdZMrRRwa4oz6wumV3m56tyS\/zeEiPri9iqitZnlVwEtHCh\/jTQwfUykbMiidVficpqPGi5k8D0iUTJ5FtFSQkVOpK9KOX7CgO\/z6958d\/ZA9SJ1XZumuTBDfFmOwI3nkOIbThjGfvs7OlHyxP6K6k3Jkygt8bLUIshJHwLOPRlFlNhZLN9mcIYRhdVMJr0ZODLjdTLB94gLYUTffsLc27JHVeBEWWtDBr8IEc3Oq7cqPZpbSTM1\/5pjlxRXYIUIlgeAbUMeg\/yHof6NyPpzjO1bxhj1YM4qmnWL34f82eO\/3sim5GcT56hm5pkFeF2vKs+JKbVNYwaMOYhcuQBCXT+Ta+hqCjColVQrJf9twy1k+aGkJrMBaKelIYaWprKGennWL\/hVQwEgOR28426euRTHdgWPsQqe46hy1FabEjiWgRYsSiIwKHszpKXEyy760BvJqeJhRC4FFDooD8grnjuoDpnK8brUhlwWlmpPDSbm02eZSPxT54zBkti5u\/EONG2Dj3EtQ6jaQ29CvxVkIHPwapyTWBMO3d4dGJ0pfhYVHLwxtd02I8eVBU471jv57cU\/P4ypYGLMt3q14MMfKVTKqSbE0lM0mVt1wPgRTolIsAbRJYdDuwrQXxFM0mo1SXjkAwdoXxlYwBnLT6rsLwQpI0VrkESygdFjuWrc\/1IDbUkoy99nlC7YmQ9drvR6h7Jhol2LuQPche+fnIy4rzx6XD6K1GFwa5Rii\/pBCvJ4AH9z3EwKhvL1Q0U1SjUiKTv69OSWfQKmjFLehtRrXx8odVFiYafxhfsskstPDkbdElT5SvLUOZOrqqdHq78LJuLC7GBRd1OPTG0VpokmSGNigRAwV+ZvlY8D\/Xqlyc92FCnl9auQGsfwmcvcgiM74YcRTKFfH4sHmIPgWxBH3\/oX5S9fn40hOORHMpQwqpdD+IvMiVS1dgKfThNT39JaMu441THkPH9ijYLJdlUv+SVtiMqnk+rZXgNsSeWSkhduGKs22wXceFM1CWIZtlvM6VL\/n8tHaOF0hk1F5GJ1kSbAG97YG8dRTU9XvcOzyStCYw1yDGH0Wo0V8iLyPefBNbueFpPcnkQ9sAt9fhDx+v4XJ6X9Vq0iNvGbZQWo8wZghAOjMPNiopJUuL4hky4rXxrp0o5uCtNJSvmgs8FUUD8Wjn5\/9ZMA4EXNvmNBxSrpifiwWx+Hrq9tUD22uFb\/8Y5BWHjSNUOeed1QG9upxKf6FXPohfDVd1Y7ECNh7J12yvrFkxo+PiUDlyC47OKzc4NlYZMZLLA4udHln4aAXEnwRm6tmK0tKbbTWkjsgWrQChT\/Soxv4a\/vqjqvwLzm1grr2iaYSYAOMRDMwRse\/h9L913RKI9+IbmRfToxgTcrSqIhBDM99H+PARRIBZtf75dEOn4yGBqEMyroPgR0mjs2dPslMOntIPYbLAUoAKydpc0FNbvmpmaX5n0N1dFB3UJZOFYKP+A5nx8kYWsKhG+nfjvMVPF7eFW7YksQfWqT8xka33ySfWhdPbtbyNWXJxKUCZSzzorc4qHQuo5GQCO3Tu\/V4TTfNENcxgE3VZDQ0qUPix4ShIx5WDn7uYtj8oP+jWuqbwu+blV45w\/u8\/yAbR5HWDfY5n3ce3yeqPlaX50jZ5iBWdY27LtYwy+R79cgxL+x\/hMMmftKoz1e5UL33JJ+Aq+clbjUSKkSK7lpuJDhCUb2MXi0jy2KkyHfP5vPcdcuqLCwc\/UPRrxjsYfIQDzryGHXbX2RoFPINnZU0uqOm2YuHz9+9zEX1ahpTKTx0y2j7OdsD2PBAJs1a8ip9j4Xx+7ZNM6Ngqvsm2cAjurtcp52yCkIuDOoVU6t5OBwCbm6SRQswT1oayjEgbUn\/Kn6+oXDrx9Vc4G\/hZk95kK1Ygu6Sh1PEeftTNXVfYkpfjRgb0UQDPpupDL83cEKC+WhSp\/3S2sMo8tUtENPX9EXO+nLEy6XZEA1FpalN6vT4ThMYUximQEGXb\/fYerhaC353Ht\/f5Dr56KGuwDPCSpL4yJJ6BloU2qgxwZiIHp8IM86k1bnMuCnY3KZltvPcXXJ6E5sDQB0Z1BLnnSnLKuAKeX6ulJ5noBkTzZe4rQwHF5cXNQx9pX6TFKxjSiu9qAB9fQYrm6Faj8FqbTVhACKSXHw7kj3DOeavgE28wyj9pO\/jfGfwV8sP3xOH1r4pM6CbuJavgXticH0X9ho6VyrRrsy74XZp1AI6FBHP6iIdA\/b7w7il8Vl7TsaZyf+xbYtCM1hIcsgCUVo50tYG3CoL+5kOjfzViZnxlJZhEGqZssp+\/kOb7Ty2ZoS0ZewjqRwOesRdEFM2ABy43qLuT7C1ulOJgYxKkAh4leb3vd0HYIXXTEkbfxrhHQ9j2dB3by6OyPbPgw8dVojeSAL\/7\/zaJoJdApKfeiV8BCHgsNdrdoVonOH4an0SD3YVuB5Yz81Z2C43WEJIUBITL\/RVnVLIGOxVmEWHD4FkwQPgmUS770Z8rqq2sZRUIGgYgKSeAGkslE4itJUQ+\/LeFwbK3WYaVa8iT4Rd74goc7Dm39h54qZ6dgel0CqgvzAngw2NmTP3aETa\/Co+eMwNyCLP+QxGEzcnqgcs63YQyqrtG4pVhQg5cMkQbRpqsytHPjg2mS+fBSAD1sXF5lrThlg6skL70o4H36abbAjOST\/ThvJ\/hViFb6lCD7w+iXDuOa0+p3sn4yhgMIqcRwKjcIxx5GsGNNSJDKWWJn7zxdT525gwSh7rf\/aGVI9PNUhlXC0bdcHQ4UjIkqSa5MQbapiDDhg1PEqDlBa0v6vqSIdDAKaxWVZNYk0Msr3cNwaAYyQfdYVCrd6CN0bFIeOWOng6klQhTYxkhmrsogCXCsMLDFaBr\/IbfQSMfwtsYiM9Jtcn18QjfumXucfKL\/F0rP0D3FKOD72idANPCy+73lYAxGSDLVmT4wmsGCEcmAtrJHCauHn65T+lSZ5d8LGFbU4P3fzP2i0+vDfPh74\/BOpx3rPgJ24chryugVZ3LRgCYrphkiiz7u+cAAsUUmcb3\/9Oy8hRdqMYIW+d8bvLygwdt\/5BjGALaF4l6kr+BuC3Zdfm3VpNgSd0yneWc81sHJVY+NTGGH6r0UEGlNOaXfv3EgCVKFIVVxQ7sdQZMlVsh\/st7NET6aPvEALHnfcC1LxmnBq4eDHNPD9Dkf2BPedenQM+nl1kzvFAn3wmRknOkW5R1yNn\/ytxx0HGmmR8Ice5wgZ4O1iiu6gYQIgfNh7iqZa6f9+uRR7OPx3G3F3FqU494Fz966O311NQWt0v9OsZcTCjdHJacQQAlrhj6ueuAjvzwPEONSf9K2l6TNYcFqw8GzppCc10C+R\/q72nSInBhsDSTw3uxgP+acqC6vthAUi6ddQSsn1n8go5+hhZ\/+BG1253M3k646Lzf3auMRmIpsm7+S1ILBZ0yoDTTh4c2+MeN2WhTaoyX2aA2SN3uNtLVEKeiZS6abtkp1VnoMP0j2IXlCkP+mii\/oqche0vGbbTphq2UUANX7KnHbbO1cOZs9kQW7JKmCzXIb9qvqF3agB8R8x++FlXXvzaE0uEX2OLHI5n\/jUgTEu+jktNbX\/fXGZmPg+T0Fcy10N1LGumnMIf4DZ3LQpBMosI\/Q7O1HfZslxjibux6HTQHMQq\/3rJ4COnsYgLhHveFY05pCAKJIX+VXbW89zVgx\/7FMBse7XVNAxZM4s+cIsPT8C4k12hqFoxBlMshjSOrF2IXLo3ha7jWl9+U4pK6wlCS3jxkIRjItzHss0Wsin9GuIAwsHo9mSpjVV88g93JOWNda7m9ClSSIBf6XVmcEVnWnRASOltW+8XKt66XviOlDQ9kc+s5j9UVKrh4EJ5y1+aVitzn95F\/7YRZqwC3WKWLAnZcYkEw2Gowg9AUg5WCbpaKoWt\/BLP+Rvklq+eWl8B9IthFtiWVVNR+j8yeJur\/5jwgrEqmRysRi2gFWhrEtA+bnx9R1llfuYjHxxPjMjrtmPRwL5\/ixnEajtuXEURjd227mnRXXuj7sYPcEb2bR18PgE4YTCkSNPlKN7tEg03TLMSk5BGtNJ4vRbfuGCWDdza7AXu8jBlF7Iz4Fr1XXwRRH1Cd4p1L6ChT\/ODh1Co4nlsbj+ttjX4g8KcHBeZJnpCySC3crd5gRRSgl1Q+Ipj3Jn9RI04uSFYW+5ju9A3ssgn9Vc5IaxH++U2R4271sbTJ3hl95flFkPeqQLFBhl2BUs7MBzuaFnhzuD5aRiYln8bHzR0Xj2jGjUtTIenvf\/ooVaYMENUwkY8hu8BYJQe183rSy7Y6dSvRZLvU7pOak2u1X0j3eDXEcFqbMpoNtJUSpGy9X4SoqD4KOcbOlP6b0y63tfZ2Y+tBQx\/MF7RbuMJQN+sgD8iBltpJsYXgatTsvUw2l\/G197UjG3FrZDROIZgnFCY\/v4E4MUm8h2Vb7c7mU2WijpNMf2XL8Lk5533OltGoenaAU68qxrcSU2+xs\/tNcLql+wwxNG1p7qC1lMRKzjfI88HdFIsaOmWPG62jzmkrZqeRp0Bp6ppeJu\/IFdmus2ELqrmewgk4GSk7cm4qK\/tE9xxYg0xD50exelnOuCzJyT5EyMa89pCHKKcr1M6rn90AD6fBq7AIRvb2GP44ADTNJrDG4ZjC+e13Flp7cf5LLWByYxe\/ku4woR1g1DeJvWZnXOwuL5Z6OwmjM05MV9GvJIeIu0f469ameZlZp9n9EOE9qhqaS1hHqbLppPSnf5TEP5ElNjbv2jIWwPJbANa0CHFf1DqDM+Lh95CYgZTPozesNUasWH6\/5drnxGhiJgqVTHM0aFFO4pEVfV49hltrzoxlOy3ZfDCDw\/qYfpN9Ej9Nw8M+zjm4yWCbpjXuHa3BnZ2yBBT\/Tsb2rqrOWSffaRMVak80px9OhdW\/MSsYqIxu2F\/1l1QlzyksAQfZREfJ5mDYMGIyD2uVjK66asosDtx2qCy3vsVdatB7sT24yiZHAfh1XfWqIv6pkJNuVYCQQNMj7Yl3fhMcDKn4sKox1W\/1XPyGPYud+rZuAVzYLXgDihHRVRy3FnOgIylhSAXyQrnS3kDFIFreD3yC\/vo98BHr\/53\/UgyJtVQEtqX2MKgZYx0qrCxGTqODZ2KL6t54bN9MTZP0kLD+uwqFx1aPwudrNffCU7\/AVwte+4HjULcubQ3bsQcYYS9jDPo1upDYvvXmOlYj4dMCiO0Hvk1X1Wd6yS6Vz9SXFud3iHsfKdKcYMzSMTXX07tAnHBU0uYS5Cipbb3sKJHhmPouj+EjNpWr6D6XwPKKL4J\/N5UzMWvjFXmeu7i7AzozgrMPhRrjyAv8hIDI9+4JiJpWQKbwWek0y4uUU9TnxvRqrvv5wgX+HFMAm36UqfUzFFbfAiULEgKPIF8jDwVp3fzZyWRtZYpc8xDOLAqM2H+rQOre7\/6LNHNwHdDceY4vQ9rJ16UB+GwShFyHRmKqP2DPV7yJG11MOOAl6l1ugKGXWz22c3ulRowRP\/qBeBarbgY="
}
When encrypting with the Kotlin code, the following data is concatenated at the end:
Size of the nonce
nonce
ciphertext/tag
When decrypting with the Python code, this data must be separated.
Note that Kotlin/Java returns as ciphertext the concatenation of actual ciphertext and tag. These two parts must also be separated, since Python processes both separately.
In addition, PBKDF2 with HMAC/SHA1 is used as the key derivation in the Kotlin code. This algorithm must also be used for decryption with the Python code.
A possible implementation in Python is:
import base64
from Crypto.Hash import SHA1
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
AES_KEY_SIZE_BIT = 256
TAG_LENGTH_BIT = 128
PBKDF2_ITERATIONS = 50000
def decrypt(passphrase, salt, ciphertext):
# Separation
ciphertextBytes = base64.b64decode(ciphertext)
ivSize = int.from_bytes(ciphertextBytes[:1], "big")
iv = ciphertextBytes[1:1+ivSize]
actualCiphertext = ciphertextBytes[1+ivSize:-TAG_LENGTH_BIT//8]
tag = ciphertextBytes[-TAG_LENGTH_BIT//8:]
# Key derivation
key = PBKDF2(passphrase, salt, AES_KEY_SIZE_BIT//8, count=PBKDF2_ITERATIONS, hmac_hash_module=SHA1)
# Decryption
cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
decrypted = cipher.decrypt_and_verify(actualCiphertext, tag)
return decrypted
passphrase = "1234".encode('utf-8')
salt = bytes.fromhex("4030cc7f15e5f7bf830543270ced2b7c8b24237c53ce95382b232df69c594353")
ciphertext = "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"
print(decrypt(passphrase, salt, ciphertext).decode('utf-8')) # {"DisabledTo_basaltest":"0","DisabledTo_breadgrams":"0","DisabledTo_dia":"0","DisabledTo_exercise":"0","DisabledTo_exercise2":"0","DisabledTo_extendedcarbs":"0","DisabledTo_hypott":"0","DisabledTo_ic":"0","DisabledTo_insulin":"0","DisabledTo_iob":"0","DisabledTo_isf":"0","DisabledTo_noisycgm":"0","DisabledTo_nsclient":"0","DisabledTo_objectives":"0","DisabledTo_objectives2":"0","DisabledTo_otherMedicationWarning":"0","DisabledTo_prerequisites":"0","DisabledTo_prerequisites2":"0","DisabledTo_profileswitch":"0","DisabledTo_profileswitch2":"0","DisabledTo_profileswitch4":"0","DisabledTo_profileswitchtime":"0","DisabledTo_pumpdisconnect":"0","DisabledTo_sensitivity":"0","DisabledTo_troubleshooting":"0","DisabledTo_update":"0","DisabledTo_wrongcarbs":"0","DisabledTo_wronginsulin":"0","ExamTask_basaltest":"false","ExamTask_breadgrams":"false","ExamTask_dia":"false","ExamTask_exercise":"false","ExamTask_exercise2":"false","ExamTask_extendedcarbs":"false","ExamTask_hypott":"false","ExamTask_ic":"false","ExamTask_insulin":"false","ExamTask_iob":"false","ExamTask_isf":"false","ExamTask_noisycgm":"false","ExamTask_nsclient":"false","ExamTask_objectives":"false","ExamTask_objectives2":"false","ExamTask_otherMedicationWarning":"false","ExamTask_prerequisites":"false","ExamTask_prerequisites2":"false","ExamTask_profileswitch":"false","ExamTask_profileswitch2":"false","ExamTask_profileswitch4":"false","ExamTask_profileswitchtime":"false","ExamTask_pumpdisconnect":"false","ExamTask_sensitivity":"false","ExamTask_troubleshooting":"false","ExamTask_update":"false","ExamTask_wrongcarbs":"false","ExamTask_wronginsulin":"false","Monitor_MainActivity_resumed":"1650398254754","Monitor_MainActivity_start":"1650398048449","Monitor_MainActivity_total":"44111","Monitor_PreferencesActivity_resumed":"1650398069260","Monitor_PreferencesActivity_start":"1650398091955","Monitor_PreferencesActivity_total":"22695","Monitor_RequestDexcomPermissionActivity_resumed":"1650398253908","Monitor_RequestDexcomPermissionActivity_start":"1650398048510","Monitor_RequestDexcomPermissionActivity_total":"505","Monitor_SetupWizardActivity_resumed":"1650398049172","Monitor_SetupWizardActivity_start":"1650398061115","Monitor_SetupWizardActivity_total":"11943","Monitor_SingleFragmentActivity_resumed":"1650398286784","Monitor_SingleFragmentActivity_start":"1650398099736","Monitor_SingleFragmentActivity_total":"19515","ObjectivesActionsUsed":"true","Objectives_auto_accomplished":"0","Objectives_auto_started":"0","Objectives_autosens_accomplished":"0","Objectives_autosens_started":"0","Objectives_config_accomplished":"0","Objectives_config_started":"0","Objectives_exam_accomplished":"0","Objectives_exam_started":"0","Objectives_maxbasal_accomplished":"0","Objectives_maxbasal_started":"0","Objectives_maxiob_accomplished":"0","Objectives_maxiob_started":"0","Objectives_maxiobzero_accomplished":"0","Objectives_maxiobzero_started":"0","Objectives_openloop_accomplished":"0","Objectives_openloop_started":"0","Objectives_smb_accomplished":"0","Objectives_smb_started":"0","Objectives_usage_accomplished":"0","Objectives_usage_started":"0","absorption_cutoff":"6","activity_duration":"0","activity_target":"0","age":"adult","always_use_shortavg":"false","application_protection":"0","aps_mode":"open","autosens_max":"1.2","autosens_min":"0.7","bolus_protection":"0","boluswizard_percentage":"100","bt_watchdog":"false","carbsReqThreshold":"1","carbs_button_increment_1":"5","carbs_button_increment_2":"10","carbs_button_increment_3":"20","dexcom_lognssensorchange":"false","dexcomg5_nsupload":"false","dexcomg5_xdripupload":"false","eatingsoon_duration":"0","eatingsoon_target":"0","enableSMB_after_carbs":"false","enableSMB_always":"false","enableSMB_with_COB":"false","enableSMB_with_high_temptarget":"false","enableSMB_with_temptarget":"false","enable_carbs_required_alert_local":"true","enable_fabric2":"true","enable_missed_bg_readings":"false","enable_pump_unreachable_alert":"true","fill_button1":"0.3","fill_button2":"0","fill_button3":"0","gradually_increase_notification_volume":"false","high_mark":"0","high_temptarget_raises_sensitivity":"false","hypo_duration":"0","hypo_target":"0","insulin_button_increment_1":"0.5","insulin_button_increment_2":"1.0","insulin_button_increment_3":"2.0","keep_screen_on":"false","key_usersuperbolus":"false","language":"default","last_revoked_certs_check":"1650398043316","last_time_this_version_detected":"1650398061747","last_versionchecker_plugin_waring":"1650398048275","loop_openmode_min_change":"20","low_mark":"0","low_temptarget_lowers_sensitivity":"false","maintenance_logs_amount":"2","maintenance_logs_email":"logs#androidaps.org","master_password":"hmac:1067c3e86cb042d799eb25e9988280703563366195f38dee2d6f1f5cc05f57e6:7b29002999b3af7dd1e5fb0b2f3f4ea96ad90a410549bf0c6e34bc9ff5e5039c","missed_bg_readings_threshold":"30","nextMissedReadingsAlarm":"1650398347880","nextPumpDisconnectedAlarm":"1650398347880","ns_alarms":"false","ns_announcements":"false","openapsama_current_basal_safety_multiplier":"4","openapsama_max_daily_safety_multiplier":"3","openapsama_min_5m_carbimpact":"8.0","openapsama_useautosens":"false","openapsma_max_basal":"1","openapsmb_max_iob":"3","pump_unreachable_threshold":"30","raise_urgent_alarms_as_android_notification":"true","resistance_lowers_target":"false","sensitivity_raises_target":"true","settings_protection":"0","short_tabtitles":"false","show_calibration_button":"true","show_carbs_button":"true","show_cgm_button":"false","show_insulin_button":"true","show_notes_entry_dialogs":"false","show_statuslights":"true","show_treatment_button":"false","show_wizard_button":"true","smbinterval":"3","smbmaxminutes":"30","smscommunicator_otp_secret":"Qz12f1m7s5Tt8Q37ZB3FYFQS9ef1p3oxtYy+t9oTQ90","startupwizard_processed":"true","statuslights_bage_critical":"240","statuslights_bage_warning":"216","statuslights_bat_critical":"5","statuslights_bat_warning":"25","statuslights_cage_critical":"72","statuslights_cage_warning":"48","statuslights_iage_critical":"144","statuslights_iage_warning":"72","statuslights_res_critical":"10","statuslights_res_warning":"80","statuslights_sage_critical":"240","statuslights_sage_warning":"216","statuslights_sbat_critical":"5","statuslights_sbat_warning":"25","treatmentssafety_maxbolus":"3","treatmentssafety_maxcarbs":"48","uamsmbmaxminutes":"30","units":"mg\/dl","use_bolus_advisor":"false","use_bolus_reminder":"false","use_smb":"false","use_uam":"false","virtualpump_type":"1","virtualpump_uploadstatus":"false"}
using the posted test data.
Note that the size of the nonce should be known on both sides so that the size doesn't need to be stored.
Also, for security reasons, the (non-secret) salt should be randomly generated like the nonce and passed to the decryption side concatenated, e.g. salt|nonce|ciphertext|tag.
I'm trying to figure out what I'm doing wrong here. I'm doing the cryptography challenges over at CryptoPals.com and this particular one has me stumped. It asks for me to create my own implementation of CTR with the following parameters:
key=YELLOW SUBMARINE
nonce=0
format=64 bit unsigned little endian nonce,
64 bit little endian block count (byte count / 16)
I've actually done the challenge successfully but I can't figure out how to do it using a library. I wanted to compare the two. I've tried PyCrypto and I've tried cryptography.hazmat. They always produce a different result. I was hoping someone could spot what I was doing wrong.
This is my most recent attempt with Crypto.Cipher:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from Crypto.Cipher import AES
from Crypto.Util import Counter
import base64
class Custom_CTR:
def __init__(self, key):
self.aes_custom = Cipher(algorithms.AES(key), modes.ECB(), default_backend())
def custom_aes_128_ctr_keystream_generator(self, nonce, counter):
while True:
to_encrypt = (nonce.to_bytes(length=8, byteorder='little')
+ counter.to_bytes(length=8, byteorder='little'))
encryptor = self.aes_custom.encryptor()
keystream_block = encryptor.update(to_encrypt) + encryptor.finalize()
yield from keystream_block
counter += 1
def custom_aes_128_ctr_encrypt(self, nonce, counter, data):
result = b''
my_gen = self.custom_aes_128_ctr_keystream_generator(nonce, counter)
for i in range(len(data)):
result += bytes([next(my_gen) ^ data[i]])
return result
def pycrypto_aes_128_ctr_encrypt(key, data):
_ctr = Counter.new(128, initial_value=0, little_endian=True)
aes = AES.new(key, AES.MODE_CTR, counter=_ctr)
return aes.encrypt(data)
k = b'YELLOW SUBMARINE'
ctr = Custom_CTR(k)
enc = base64.b64decode(b'L77na/nrFsKvynd6HzOoG7GHTLXsTVu9qvY/2syLXzhPweyyMTJULu/6/kXX0KSvoOLSFQ==')
print('custom ctr:', ctr.custom_aes_128_ctr_encrypt(0, 0, enc))
print('hazmat ctr:', pycrypto_aes_128_ctr_encrypt(k, enc))
Produces
custom ctr: b"Yo, VIP Let's kick it Ice, Ice, baby Ice, Ice, baby "
hazmat ctr: b"Yo, VIP Let's kic\x84q\x97A\xae\xdcZ#\xf2\xc4\xafc\xb1\xd8\xad8\xfb|\xb4\xd4\x17\x95\x9cX\x0ff\xb6\xf3\xb8\xb3Z\xfe\xff\x9b6"
Nevermind, I just solved it.
def pycrypto_aes_128_ctr_encrypt(key, data):
_ctr = Counter.new(64, initial_value=0, prefix=b'\x00'*8, little_endian=True)
aes = AES.new(key, AES.MODE_CTR, counter=_ctr)
return aes.encrypt(data)
Looks like this generates the parameters that I'm looking for. The counter is 8 bytes and the prefix (nonce) is 8 bytes.
Is it possible to encrypt a message with a private key in python using pycryptodome or any other library? I know that you are not supposed to encrypt with the private key and decrypt with the public key, but my purpose is to encrypt with the private one so the receiver could be sure that the message was send by the real author. More than secure encryption I'm looking for some kind of obfuscation. I want to do an app where the message is public but it can only be seen if you have the public key.
I've tried to do this:
from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64
def generate_keys():
modulus_lenght = 256 * 4
private_key = RSA.generate(modulus_lenght, Random.new().read)
public_key = private_key.publickey()
return private_key, public_key
def encrypt_private_key(a_message, private_key):
encryptor = PKCS1_OAEP.new(private_key)
encrypted_msg = encryptor.encrypt(a_message)
encoded_encrypted_msg = base64.b64encode(encrypted_msg)
return encoded_encrypted_msg
def decrypt_public_key(encoded_encrypted_msg, public_key):
encryptor = PKCS1_OAEP.new(public_key)
decoded_encrypted_msg = base64.b64decode(encoded_encrypted_msg)
decoded_decrypted_msg = encryptor.decrypt(decoded_encrypted_msg)
return decoded_decrypted_msg
private_key, public_key = generate_keys()
message = "Hello world"
encoded = encrypt_private_key(message, private_key)
decoded = decrypt_public_key(encoded, public_key)
print decoded
But it raises the next error: TypeError: This is not a private key.
Short answer
the code that you are using doesn't allow you to do that for security reasons
alternative code below
Long answer
I was curious about your problem and then I started to try to code
After a while I realized that if you run this snippet you will see that it correctly works:
#!/usr/bin/env python
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64
def generate_keys():
modulus_length = 1024
key = RSA.generate(modulus_length)
#print (key.exportKey())
pub_key = key.publickey()
#print (pub_key.exportKey())
return key, pub_key
def encrypt_private_key(a_message, private_key):
encryptor = PKCS1_OAEP.new(private_key)
encrypted_msg = encryptor.encrypt(a_message)
print(encrypted_msg)
encoded_encrypted_msg = base64.b64encode(encrypted_msg)
print(encoded_encrypted_msg)
return encoded_encrypted_msg
def decrypt_public_key(encoded_encrypted_msg, public_key):
encryptor = PKCS1_OAEP.new(public_key)
decoded_encrypted_msg = base64.b64decode(encoded_encrypted_msg)
print(decoded_encrypted_msg)
decoded_decrypted_msg = encryptor.decrypt(decoded_encrypted_msg)
print(decoded_decrypted_msg)
#return decoded_decrypted_msg
def main():
private, public = generate_keys()
print (private)
message = b'Hello world'
encoded = encrypt_private_key(message, public)
decrypt_public_key(encoded, private)
if __name__== "__main__":
main()
but if you now change two of the final lines [i.e. the role of the keys] into:
encoded = encrypt_private_key(message, private)
decrypt_public_key(encoded, public)
and rerun the program you will get the TypeError: No private key
Let me quote from this great answer:
"As it turns out, PyCrypto is only trying to prevent you from mistaking one for the other here, OpenSSL or Ruby OpenSSL allow you for example to do both: public_encrypt/public_decrypt and private_encrypt/private_decrypt
[...]
Additional things need to be taken care of to make the result usable in practice. And that's why there is a dedicated signature package in PyCrypto - this effectively does what you described, but also additionally takes care of the things I mentioned"
Adapting this link I came to the following code that should solve your question:
# RSA helper class for pycrypto
# Copyright (c) Dennis Lee
# Date 21 Mar 2017
# Description:
# Python helper class to perform RSA encryption, decryption,
# signing, verifying signatures & keys generation
# Dependencies Packages:
# pycrypto
# Documentation:
# https://www.dlitz.net/software/pycrypto/api/2.6/
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA512, SHA384, SHA256, SHA, MD5
from Crypto import Random
from base64 import b64encode, b64decode
import rsa
hash = "SHA-256"
def newkeys(keysize):
random_generator = Random.new().read
key = RSA.generate(keysize, random_generator)
private, public = key, key.publickey()
return public, private
def importKey(externKey):
return RSA.importKey(externKey)
def getpublickey(priv_key):
return priv_key.publickey()
def encrypt(message, pub_key):
#RSA encryption protocol according to PKCS#1 OAEP
cipher = PKCS1_OAEP.new(pub_key)
return cipher.encrypt(message)
def decrypt(ciphertext, priv_key):
#RSA encryption protocol according to PKCS#1 OAEP
cipher = PKCS1_OAEP.new(priv_key)
return cipher.decrypt(ciphertext)
def sign(message, priv_key, hashAlg="SHA-256"):
global hash
hash = hashAlg
signer = PKCS1_v1_5.new(priv_key)
if (hash == "SHA-512"):
digest = SHA512.new()
elif (hash == "SHA-384"):
digest = SHA384.new()
elif (hash == "SHA-256"):
digest = SHA256.new()
elif (hash == "SHA-1"):
digest = SHA.new()
else:
digest = MD5.new()
digest.update(message)
return signer.sign(digest)
def verify(message, signature, pub_key):
signer = PKCS1_v1_5.new(pub_key)
if (hash == "SHA-512"):
digest = SHA512.new()
elif (hash == "SHA-384"):
digest = SHA384.new()
elif (hash == "SHA-256"):
digest = SHA256.new()
elif (hash == "SHA-1"):
digest = SHA.new()
else:
digest = MD5.new()
digest.update(message)
return signer.verify(digest, signature)
def main():
msg1 = b"Hello Tony, I am Jarvis!"
msg2 = b"Hello Toni, I am Jarvis!"
keysize = 2048
(public, private) = rsa.newkeys(keysize)
# https://docs.python.org/3/library/base64.html
# encodes the bytes-like object s
# returns bytes
encrypted = b64encode(rsa.encrypt(msg1, private))
# decodes the Base64 encoded bytes-like object or ASCII string s
# returns the decoded bytes
decrypted = rsa.decrypt(b64decode(encrypted), private)
signature = b64encode(rsa.sign(msg1, private, "SHA-512"))
verify = rsa.verify(msg1, b64decode(signature), public)
#print(private.exportKey('PEM'))
#print(public.exportKey('PEM'))
print("Encrypted: " + encrypted.decode('ascii'))
print("Decrypted: '%s'" % (decrypted))
print("Signature: " + signature.decode('ascii'))
print("Verify: %s" % verify)
rsa.verify(msg2, b64decode(signature), public)
if __name__== "__main__":
main()
Final notes:
the last prints have ascii because as stated here "In case of base64 however, all characters are valid ASCII characters"
in this case we are using the same key - the private one - both for encrypting and decrypting, so yes: we would end up to be symmetric but...
but - as stated here - "The public key is PUBLIC - it's something you would readily share and thus would be easily disseminated. There's no added value in that case compared to using a symmetric cipher and a shared key" plus "Conceptually, "encrypting" with the private key is more useful for signing a message whereas the "decryption" using the public key is used for verifying the message"
the same identical last principle is expressed in this answer - "Typically [...] we say sign with the private key and verify with the public key"
Looks like pycrypto has not been under active development since 2014 and support ended at python 3.3. cryptography seems like the standard now.
Using cryptography:
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
password = b'thepassword'
key = rsa.generate_private_key(
backend=default_backend(),
public_exponent=65537,
key_size=2048
)
private_key = key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.BestAvailableEncryption(password)
)
public_key = key.public_key().public_bytes(
serialization.Encoding.OpenSSH,
serialization.PublicFormat.OpenSSH
)
What you are describing is called message signing and it uses private/public keys to verify that the message did come from the claimed sender and that it has not been tampered with en route. You don't have to "invent" these methods ...
https://medium.com/#securegns/implementing-asymmetric-encryption-to-secure-your-project-35368049cb5f
I have server written in python and client in C . Their job is to send a secret message from server to client which is encrypted with RSA private key. I am using openssl/rsa.h library, that is I initialize a rsa object with a private key and encrypte a message with RSA_public_encrypt(length_of_message, "Secret Message", to, rsa, RSA_PKCS1_PADDING) . Then I send this encrypted message to python server and try to decrypt it with same private key using from Crypto.PublicKey import RSA library. Problem is that it does not decrypt it properly. It always outputs 128-bit length message where the secret message is randomly placed in it (e.g. '\x23\xa3x\43...Secret Message\xef\x4a'), where it should normally return just Secret Message.
The problem was about the padding. Python's rsa module decrypts result with PKCS1 padding and does not removes padding. With the function below which I have taken from here problem was solved:
def pkcs1_unpad(text):
if len(text) > 0 and text[0] == '\x02':
# Find end of padding marked by nul
pos = text.find('\x00')
if pos > 0:
return text[pos+1:]
return None
Is it possible to create a same pair of RSA key in Python and C . please find the code below and let me know if any modification needed to get it worked.
Code in python
key = RSA.generate(2048)
file_out_pub = open("pubkey.der", "wb")
file_out_pub.write(key.publickey().exportKey())
file_out_pub.close()
file_out_pub = open("pubkey.der", "`enter code here`r")
public_key = RSA.importKey(file_out_pub.read())
cipher = PKCS1_OAEP.new(public_key)
password = pw
ciphertext = cipher.encrypt(password)
Code in C
int clen = 0, num, ret;
clen = strnlen_s(req->pw,2048);
unsigned char ptext[2048];
RSA *rsa = RSA_new();
BIGNUM *e = BN_new();
ret = RSA_generate_key_ex(rsa, 2048, e, NULL );
num = RSA_private_decrypt(clen, req->pw , ptext, rsa, RSA_PKCS1_OAEP_PADDING);
// Start authentication process
strncpy(req->pw,ptext,MAX_PASSWORD_STR);