/*
    This Class manages the encryption for the turnover value, which has to be included within the QR-Code.
    For testing purpose, a ::ROUTINE which decrypts the value has been included.
*/
::CLASS AESHandler PUBLIC
::ATTRIBUTE Cipher
::ATTRIBUTE SecretKeySpec
::ATTRIBUTE KeyGenerator
::ATTRIBUTE StandardCharsets
::ATTRIBUTE Base64
::ATTRIBUTE IvParameterSpec
::ATTRIBUTE IV
::ATTRIBUTE MessageDigest
::METHOD init
 EXPOSE Cipher SecretKeySpec KeyGenerator StandardCharsets Base64 IvParameterSpec MessageDigest
 Cipher = bsf.import("javax.crypto.Cipher")
 SecretKeySpec = bsf.import("javax.crypto.spec.SecretKeySpec")
 KeyGenerator = bsf.import("javax.crypto.KeyGenerator")
 Base64 = bsf.import("java.util.Base64")
 StandardCharsets = bsf.import("java.nio.charset.StandardCharsets")
 IvParameterSpec = bsf.import("javax.crypto.spec.IvParameterSpec")
 MessageDigest = bsf.loadClass("java.security.MessageDigest")
------------------------------------------------------------------------------------------------------------------------
::METHOD encryptAES PUBLIC
 EXPOSE Cipher  SecretKeySpec Base64 IvParameterSpec IV
 USE ARG turnover, secretKey, cashID, receiptID, turnOverCounterLengthInBytes = 5
 ByteBuffer = bsf.loadClass("java.nio.ByteBuffer")
 Decoder = Base64~getDecoder
 Encoder = Base64~getEncoder
 decodedSecKey = Decoder~decode(secretKey)

 IV = self~getIV(cashID,receiptID) -- IV = byteArray

 byteBufferIV = ByteBuffer~allocate(16)
 byteBufferIV~put(IV)
 IV = byteBufferIV~array

 turnover = box("Long",turnover)
 byteBufferData = ByteBuffer~allocate(16)
 byteBufferData~putLong(turnover)
 data = byteBufferData~array

 turnOverCounterByteRep = self~get2ComplementRepForLong(turnover,turnOverCounterLengthInBytes)
 bsf.loadClass("java.lang.System")~arraycopy(turnOverCounterByteRep, 0, data, 0, turnOverCounterByteRep~size)

 IVspec = IvParameterSpec~new(IV)
 aesKey = SecretKeySpec~new(decodedSecKey, "AES")
 cipher = Cipher~getInstance("AES/CTR/NoPadding")
 cipher~bsf.invoke("init", Cipher~ENCRYPT_MODE, aesKey, IVspec)
 encryptedTurnOverValueComplete = cipher~doFinal(data)
 encryptedTurnOverValue = bsf.CreateJavaArray("byte.class", turnOverCounterLengthInBytes)

 bsf.loadClass("java.lang.System")~arraycopy(encryptedTurnOverValueComplete, 0, encryptedTurnOverValue, 0, turnOverCounterLengthInBytes)
 encryptedB64 = Encoder~encodeToString(encryptedTurnOverValue)    -- to get base64-String and no byte array
 RETURN encryptedB64
------------------------------------------------------------------------------------------------------------------------
::METHOD getIV PUBLIC
 EXPOSE StandardCharsets MessageDigest
 USE ARG cashboxID, receiptID

 utf8cashboxID = StandardCharsets~UTF_8~encode(cashboxID)
 utf8receiptID = StandardCharsets~UTF_8~encode(receiptID)

 utf8IV_chain = utf8cashboxID || utf8receiptID

 digest = MessageDigest~getInstance("SHA-256") -- returns 32 byte
 encodedHash = digest~digest(BsfRawBytes(utf8IV_chain))
 encodedHashExt = bsf.createJavaArray("byte.class", 16)
 bsf.loadClass("java.lang.System")~arraycopy(encodedHash, 0, encodedHashExt, 0, 16) -- extracting first 16 byte of 32 as IV
 RETURN encodedHashExt
------------------------------------------------------------------------------------------------------------------------
::METHOD decryptAES PUBLIC
 EXPOSE Cipher  SecretKeySpec Base64 IvParameterSpec IV
 USE ARG encryptedTurnOverValueInB64, secretKey, cashID, receiptID, turnOverCounterLengthInBytes = 5
 ByteBuffer = bsf.loadClass("java.nio.ByteBuffer")

 Decoder = Base64~getDecoder
 Encoder = Base64~getEncoder
 secretKey = Decoder~decode(secretKey)
 decodedTurnOverValue = Decoder~decode(encryptedTurnOverValueInB64)

 byteBufferIV = ByteBuffer~allocate(16)
 byteBufferIV~put(IV)
 IV = byteBufferIV~array

 IV = IvParameterSpec~new(IV)                            --use same IV to decode AES
 aesKey = SecretKeySpec~new(secretKey, "AES")
 cipher = Cipher~getInstance("AES/CTR/NoPadding")
 cipher~bsf.invoke("init", Cipher~DECRYPT_MODE, aesKey, IV)
 turnover = cipher~doFinal(decodedTurnOverValue)
 textB64 = Encoder~encodeToString(turnover)    -- to get base64-String and no byte array
 RETURN self~getLong(turnover)
------------------------------------------------------------------------------------------------------------------------
::METHOD hashLastReceipt PUBLIC
 EXPOSE MessageDigest StandardCharsets Base64
 USE ARG text, N=8
 Encoder = Base64~getEncoder

 javaString = box("STring", text)
 digest = MessageDigest~getInstance("SHA-256")
 hash = digest~digest(javaString~getBytes(StandardCharsets~UTF_8))
 conDigest = bsf.CreateJavaArray("byte.class", N)
 bsf.loadClass("java.lang.System")~arraycopy(hash, 0, conDigest, 0, N)
 RETURN Encoder~encodeToString(conDigest)
------------------------------------------------------------------------------------------------------------------------
::METHOD get2ComplementRepForLong PUBLIC
 USE ARG value, numberOfBytesFor2ComplementRepresentation
 ByteBuffer = bsf.loadClass("java.nio.ByteBuffer")
 byteBufferArray = ByteBuffer~allocate(8)

 byteBufferArray~putLong(value)
 longRep = byteBufferArray~array

IF numberOfBytesFor2ComplementRepresentation = 8 THEN DO
    RETURN longRep
END
 byteRep = bsf.CreateJavaArray("byte.class", numberOfBytesFor2ComplementRepresentation)

 bsf.loadClass("java.lang.System")~arraycopy(longRep, 8-numberOfBytesFor2ComplementRepresentation, byteRep,0,numberOfBytesFor2ComplementRepresentation)
 RETURN byteRep
------------------------------------------------------------------------------------------------------------------------
::METHOD getLong PUBLIC
 USE ARG bytes

 BigInteger = bsf.import("java.math.BigInteger")~new(bytes)
 RETURN BigInteger~longValue
------------------------------------------------------------------------------------------------------------------------
::METHOD generateRandomAESKey PUBLIC
 EXPOSE KeyGenerator Base64

 keyGenAES = KeyGenerator~getInstance("AES")
 keyGenAES~bsf.invoke("init", 256)
 secretKey = keyGenAES~generateKey
 encodedSecKey = secretKey~getEncoded
 Encoder = Base64~getEncoder
 base64Key = Encoder~encodeToString(encodedSecKey)
 RETURN base64Key
 ------------------------------------------------------------------------------------------------------------------------
::REQUIRES "BSF.CLS"

/*   License:

     ------------------------ Apache Version 2.0 license -------------------------
        Copyright (C) 2021 Manuel Schwarzer

        Licensed under the Apache License, Version 2.0 (the "License");
        you may not use this file except in compliance with the License.
        You may obtain a copy of the License at

            http://www.apache.org/licenses/LICENSE-2.0

        Unless required by applicable law or agreed to in writing, software
        distributed under the License is distributed on an "AS IS" BASIS,
        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        See the License for the specific language governing permissions and
        limitations under the License.
     -----------------------------------------------------------------------------
 */
