Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Aes encryption compatible with openssl cmd
Forum Updated to NodeBB v4.3 + New Features

Aes encryption compatible with openssl cmd

Scheduled Pinned Locked Moved Solved General and Desktop
14 Posts 3 Posters 1.9k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • SGaistS Offline
    SGaistS Offline
    SGaist
    Lifetime Qt Champion
    wrote on last edited by
    #2

    Hi,

    @Marek said in Aes encryption compatible with openssl cmd:

    QString arg("letmein");
    int len = arg.toLocal8Bit().length()+1;
    auto plainText = reinterpret_cast<unsigned char*>(const_cast<char*>(static_cast<const char*>(arg.toLatin1())));

    Why are you using two different encoding ?

    plainText does not point what you think. The toLatin1 method returns a QByteArray, not a char pointer.

    Interested in AI ? www.idiap.ch
    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

    M 1 Reply Last reply
    1
    • SGaistS SGaist

      Hi,

      @Marek said in Aes encryption compatible with openssl cmd:

      QString arg("letmein");
      int len = arg.toLocal8Bit().length()+1;
      auto plainText = reinterpret_cast<unsigned char*>(const_cast<char*>(static_cast<const char*>(arg.toLatin1())));

      Why are you using two different encoding ?

      plainText does not point what you think. The toLatin1 method returns a QByteArray, not a char pointer.

      M Offline
      M Offline
      Marek
      wrote on last edited by
      #3

      @SGaist Hi
      I took this example from forum https://forum.qt.io/topic/96587/qt-with-openssl-aes-256-cbc-encryption/4

      when I change this line to:

      unsigned char* plainText=reinterpret_cast<unsigned char *>(arg.toLocal8Bit().data());
      OR
      unsigned char plainText[]="letmein";
      

      Output is the same as previous

      BEGIN openssl2
      EncryptMgr::openssl2 ciphertext: "f602bdec9c84c0d3476ebf6115588131"  base: "9gK97JyEwNNHbr9hFViBMQ=="
      EncryptMgr::openssl2 decryptText: "letmein"
      END openssl2
      

      Here I'm testing all known (to me) combination of openssl enc cmd

      enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1
      IAa9TSIdLCIU7XRDiHemxg==
      enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5
      Uxj0FgyEY3Bc1XDaaoIsTw==
      enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md md5
      fdDcCauSOiGYGNmUPnFaLg==
      enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md md5
      wa5TR/Vbe3spT7RhVKxX3Q==
      enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md sha1
      HFL8j/EPy/Q5NGoy5Ujkcw==
      enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md sha1
      iwY4XIcGLATKRyyP+n377w==
      enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2
      9Mjk39/gvgO42P3nuVzKHg==
      enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md md5
      J5bUApllT5MCB2hAukm9Zw==
      enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1
      1mt3AKZ9fpLCgLIlPIOkHA==
      enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1
      Bd6B3CUO0nn6thX+Cs3nWw==
      enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5
      jl4vRGtTxItse8xHf4vygQ==
      enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md md5
      tCFPTf5AkS34DOkPcPK6aQ==
      enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md md5
      I0r5J3ZnlctvH9bjphaDQg==
      enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md sha1
      tWpHRO2gyhkIj04fx8f51A==
      enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md sha1
      eWHrPFxtOcbN3eVy2eUzOA==
      enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2
      zkcvwZ+6q8NEmS4my2TFJQ==
      enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md md5
      tZzIRZ7crQ38LDLYxyz3tA==
      enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1
      iZ5IdMK9pkAg0HRF38mHNQ==
      enc_test$
      

      complete madness ;)
      Best,
      Marek

      Christian EhrlicherC 1 Reply Last reply
      0
      • M Marek

        @SGaist Hi
        I took this example from forum https://forum.qt.io/topic/96587/qt-with-openssl-aes-256-cbc-encryption/4

        when I change this line to:

        unsigned char* plainText=reinterpret_cast<unsigned char *>(arg.toLocal8Bit().data());
        OR
        unsigned char plainText[]="letmein";
        

        Output is the same as previous

        BEGIN openssl2
        EncryptMgr::openssl2 ciphertext: "f602bdec9c84c0d3476ebf6115588131"  base: "9gK97JyEwNNHbr9hFViBMQ=="
        EncryptMgr::openssl2 decryptText: "letmein"
        END openssl2
        

        Here I'm testing all known (to me) combination of openssl enc cmd

        enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1
        IAa9TSIdLCIU7XRDiHemxg==
        enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5
        Uxj0FgyEY3Bc1XDaaoIsTw==
        enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md md5
        fdDcCauSOiGYGNmUPnFaLg==
        enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md md5
        wa5TR/Vbe3spT7RhVKxX3Q==
        enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md sha1
        HFL8j/EPy/Q5NGoy5Ujkcw==
        enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md sha1
        iwY4XIcGLATKRyyP+n377w==
        enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2
        9Mjk39/gvgO42P3nuVzKHg==
        enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md md5
        J5bUApllT5MCB2hAukm9Zw==
        enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1
        1mt3AKZ9fpLCgLIlPIOkHA==
        enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1
        Bd6B3CUO0nn6thX+Cs3nWw==
        enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5
        jl4vRGtTxItse8xHf4vygQ==
        enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md md5
        tCFPTf5AkS34DOkPcPK6aQ==
        enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md md5
        I0r5J3ZnlctvH9bjphaDQg==
        enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md sha1
        tWpHRO2gyhkIj04fx8f51A==
        enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md sha1
        eWHrPFxtOcbN3eVy2eUzOA==
        enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2
        zkcvwZ+6q8NEmS4my2TFJQ==
        enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md md5
        tZzIRZ7crQ38LDLYxyz3tA==
        enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1
        iZ5IdMK9pkAg0HRF38mHNQ==
        enc_test$
        

        complete madness ;)
        Best,
        Marek

        Christian EhrlicherC Online
        Christian EhrlicherC Online
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on last edited by
        #4

        @Marek said in Aes encryption compatible with openssl cmd:

        arg.toLocal8Bit().data()

        You've working on a temporary. Basic c++ error.

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        1 Reply Last reply
        1
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #5

          @Marek said in Aes encryption compatible with openssl cmd:

          unsigned char* plainText=reinterpret_cast<unsigned char *>(arg.toLocal8Bit().data());

          You are taking the data pointer of a temporary QByteArray which lifetime ends after the semi colon therefore pointing to somewhere that may or may not contain the data.

          You have to ensure that the lifetime of your objects.

          As already written numerous time:

          QString someString("Foo bar");
          QByteArray latin1Array = someString.toLatin1();
          // Now you can use your pointer thingy based on latin1Array.data()
          

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          M 1 Reply Last reply
          1
          • SGaistS SGaist

            @Marek said in Aes encryption compatible with openssl cmd:

            unsigned char* plainText=reinterpret_cast<unsigned char *>(arg.toLocal8Bit().data());

            You are taking the data pointer of a temporary QByteArray which lifetime ends after the semi colon therefore pointing to somewhere that may or may not contain the data.

            You have to ensure that the lifetime of your objects.

            As already written numerous time:

            QString someString("Foo bar");
            QByteArray latin1Array = someString.toLatin1();
            // Now you can use your pointer thingy based on latin1Array.data()
            
            M Offline
            M Offline
            Marek
            wrote on last edited by
            #6

            @SGaist @Christian-Ehrlicher thanks for your help

            but no difference

            QString arg("letmein");
            QByteArray latin1Array = arg.toLatin1();
            unsigned char* plainText=reinterpret_cast<unsigned char *>(latin1Array.data());
            

            Result is the same:

            BEGIN openssl2
            EncryptMgr::openssl2 ciphertext: "f602bdec9c84c0d3476ebf6115588131"  base: "9gK97JyEwNNHbr9hFViBMQ=="
            EncryptMgr::openssl2 decryptText: "letmein"
            END openssl2
            

            And I even tested just hardcoding string letmein as unsinged char (same result)

            unsigned char plainText[]="letmein"
            

            But maybe there is some error with reading cipher text from unsigned char to QByteArray ?

            Best
            Marek

            1 Reply Last reply
            0
            • Christian EhrlicherC Online
              Christian EhrlicherC Online
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on last edited by
              #7

              There is so much code which is not needed so I don't understand what's really going on. For such a simple encryption not more than 10 lines should be needed. Please provide a minimal compilable example.
              And are you sure the \0 is added on command line? I would guess no.

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              M 1 Reply Last reply
              1
              • Christian EhrlicherC Christian Ehrlicher

                There is so much code which is not needed so I don't understand what's really going on. For such a simple encryption not more than 10 lines should be needed. Please provide a minimal compilable example.
                And are you sure the \0 is added on command line? I would guess no.

                M Offline
                M Offline
                Marek
                wrote on last edited by
                #8

                @Christian-Ehrlicher
                There are three types of encrypt/decrypt atempts in this code thats why it looks big,
                I can't shrink it unless I remove one of methods

                1. openssl2 function is using:
                  aes_init - to init openssl and EVP_BytesToKey to create key from password
                  aes_encrypt - to encypt
                  aes_decrypt - to decrypt
                  openssl2 is initializing aes, performs encrypt and decrypt and prints the result

                2. openssl function is using:
                  encrypt - to encrypt
                  decrypt - to decrypt
                  handleErrors - doesn't matter
                  This one is different in terms of key handling, does not derive key from password, key is already provided, taken from openssl command

                openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1 -p
                
                1. This is just using https://github.com/bricke/Qt-AES
                  all in single procedure

                Best,
                Marek

                M 1 Reply Last reply
                0
                • M Marek

                  @Christian-Ehrlicher
                  There are three types of encrypt/decrypt atempts in this code thats why it looks big,
                  I can't shrink it unless I remove one of methods

                  1. openssl2 function is using:
                    aes_init - to init openssl and EVP_BytesToKey to create key from password
                    aes_encrypt - to encypt
                    aes_decrypt - to decrypt
                    openssl2 is initializing aes, performs encrypt and decrypt and prints the result

                  2. openssl function is using:
                    encrypt - to encrypt
                    decrypt - to decrypt
                    handleErrors - doesn't matter
                    This one is different in terms of key handling, does not derive key from password, key is already provided, taken from openssl command

                  openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1 -p
                  
                  1. This is just using https://github.com/bricke/Qt-AES
                    all in single procedure

                  Best,
                  Marek

                  M Offline
                  M Offline
                  Marek
                  wrote on last edited by
                  #9

                  in terms of adding '\0'
                  I think echo command has a switch -n which tells whether to add \n to the end of the string, I tried both options

                  M 1 Reply Last reply
                  0
                  • Christian EhrlicherC Online
                    Christian EhrlicherC Online
                    Christian Ehrlicher
                    Lifetime Qt Champion
                    wrote on last edited by
                    #10

                    As I said - your code isn't very readable. Please provide one example, no classes, nothing around so we can actually compile it here.

                    Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                    Visit the Qt Academy at https://academy.qt.io/catalog

                    M 1 Reply Last reply
                    1
                    • Christian EhrlicherC Christian Ehrlicher

                      As I said - your code isn't very readable. Please provide one example, no classes, nothing around so we can actually compile it here.

                      M Offline
                      M Offline
                      Marek
                      wrote on last edited by
                      #11

                      @Christian-Ehrlicher ok just a minute

                      best,
                      Marek

                      1 Reply Last reply
                      0
                      • M Marek

                        in terms of adding '\0'
                        I think echo command has a switch -n which tells whether to add \n to the end of the string, I tried both options

                        M Offline
                        M Offline
                        Marek
                        wrote on last edited by
                        #12

                        As I understand this should give the same result as openssl command since it is using key taken from openssl command

                        $ echo  "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1 -p
                        key=580D5BAA02014A40B0E30C6E82B60EA5
                        iZ5IdMK9pkAg0HRF38mHNQ==
                        $ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1 -p
                        key=580D5BAA02014A40B0E30C6E82B60EA5
                        1mt3AKZ9fpLCgLIlPIOkHA==
                        
                        void EncryptMgr::opensslShort() {
                            qDebug()<<"BEGIN openssl";
                            /* A 128 bit key */
                        
                            unsigned char *key = (unsigned char *)"580D5BAA02014A40B0E30C6E82B60EA5";
                            /* Message to be encrypted */
                            unsigned char *plaintext =
                                    (unsigned char *)"letmein";
                        
                            /* Buffer for ciphertext. Ensure the buffer is long enough for the
                               * ciphertext which may be longer than the plaintext, dependant on the
                               * algorithm and mode
                               */
                            unsigned char ciphertext[128];
                        
                            int ciphertext_len;
                            int len;
                            int plaintext_len=strlen ((char *)plaintext);
                            EVP_CIPHER_CTX *ctx;
                        
                            /* Initialise the library */
                            ERR_load_crypto_strings();
                            OpenSSL_add_all_algorithms();
                            OPENSSL_config(NULL);
                        
                            /* Encrypt the plaintext */
                            if(!(ctx = EVP_CIPHER_CTX_new()))
                                qDebug()<<"some errors";
                        
                            /* Initialise the encryption operation. IMPORTANT - ensure you use a key
                               * In this example we are using 128 bit AES (i.e. a 128 bit key).
                              */
                            if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key,NULL))
                                qDebug()<<"some errors";
                        
                            /* Provide the message to be encrypted, and obtain the encrypted output.
                               * EVP_EncryptUpdate can be called multiple times if necessary
                               */
                            if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
                                qDebug()<<"some errors";
                            ciphertext_len = len;
                        
                            /* Finalise the encryption. Further ciphertext bytes may be written at
                               * this stage.
                               */
                            if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))  handleErrors();
                            ciphertext_len += len;
                        
                            /* Do something useful with the ciphertext here */
                        
                            QByteArray arr;
                            for(int i=0;i<ciphertext_len;i++) {
                                arr[i]=ciphertext[i];
                            }
                            QByteArray str_cipher=QByteArray::fromRawData(reinterpret_cast<char*>(ciphertext), ciphertext_len);
                            qDebug()<<"EncryptMgr::openssl str_cipher:"<<str_cipher.toBase64()<<" second method:"<<arr.toBase64()<<" len:"<<ciphertext_len;
                        
                            EVP_CIPHER_CTX_free(ctx);
                        
                            /* Clean up */
                            EVP_cleanup();
                            ERR_free_strings();
                        }
                        

                        Best,
                        Marek

                        1 Reply Last reply
                        0
                        • Christian EhrlicherC Online
                          Christian EhrlicherC Online
                          Christian Ehrlicher
                          Lifetime Qt Champion
                          wrote on last edited by
                          #13

                          This is a minimal example which also prints out the same as the command line:

                          auto ciphertext = reinterpret_cast<const unsigned char*>("letmein");
                          const int c_len = strlen(reinterpret_cast<const char*>(ciphertext));
                          int f_len1 = 0;
                          int f_len2 = 0;
                          auto keyIn = reinterpret_cast<const unsigned char*>("myPassword");
                          int k_len = strlen(reinterpret_cast<const char*>(keyIn));
                          unsigned char keyOut1[16];
                           
                          PKCS5_PBKDF2_HMAC_SHA1("myPassword", k_len, nullptr, 0, 5, sizeof(keyOut1), keyOut1);
                          EVP_CIPHER_CTX* e = EVP_CIPHER_CTX_new();
                          EVP_CIPHER_CTX_init(e);
                          EVP_EncryptInit_ex(e, EVP_aes_128_ecb(), nullptr, keyOut1, nullptr);
                          std::vector<unsigned char> dataOut(c_len + EVP_CIPHER_CTX_block_size(e));
                          EVP_EncryptUpdate(e, dataOut.data(), &f_len1, ciphertext, c_len);
                          EVP_EncryptFinal_ex(e, dataOut.data() + f_len1, &f_len2);
                          EVP_CIPHER_CTX_free(e);
                          qDebug() << QByteArray::fromRawData(reinterpret_cast<char*>(dataOut.data()), f_len1 + f_len2).toBase64();
                          

                          --> "iwY4XIcGLATKRyyP+n377w=="

                          enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md sha1
                          iwY4XIcGLATKRyyP+n377w==
                          

                          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                          Visit the Qt Academy at https://academy.qt.io/catalog

                          M 1 Reply Last reply
                          3
                          • Christian EhrlicherC Christian Ehrlicher

                            This is a minimal example which also prints out the same as the command line:

                            auto ciphertext = reinterpret_cast<const unsigned char*>("letmein");
                            const int c_len = strlen(reinterpret_cast<const char*>(ciphertext));
                            int f_len1 = 0;
                            int f_len2 = 0;
                            auto keyIn = reinterpret_cast<const unsigned char*>("myPassword");
                            int k_len = strlen(reinterpret_cast<const char*>(keyIn));
                            unsigned char keyOut1[16];
                             
                            PKCS5_PBKDF2_HMAC_SHA1("myPassword", k_len, nullptr, 0, 5, sizeof(keyOut1), keyOut1);
                            EVP_CIPHER_CTX* e = EVP_CIPHER_CTX_new();
                            EVP_CIPHER_CTX_init(e);
                            EVP_EncryptInit_ex(e, EVP_aes_128_ecb(), nullptr, keyOut1, nullptr);
                            std::vector<unsigned char> dataOut(c_len + EVP_CIPHER_CTX_block_size(e));
                            EVP_EncryptUpdate(e, dataOut.data(), &f_len1, ciphertext, c_len);
                            EVP_EncryptFinal_ex(e, dataOut.data() + f_len1, &f_len2);
                            EVP_CIPHER_CTX_free(e);
                            qDebug() << QByteArray::fromRawData(reinterpret_cast<char*>(dataOut.data()), f_len1 + f_len2).toBase64();
                            

                            --> "iwY4XIcGLATKRyyP+n377w=="

                            enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md sha1
                            iwY4XIcGLATKRyyP+n377w==
                            
                            M Offline
                            M Offline
                            Marek
                            wrote on last edited by
                            #14

                            @Christian-Ehrlicher Thanks man, this works for me ;)
                            When I find some time I try to find bug in my code.

                            Best Regards,
                            Marek

                            1 Reply Last reply
                            0

                            • Login

                            • Login or register to search.
                            • First post
                              Last post
                            0
                            • Categories
                            • Recent
                            • Tags
                            • Popular
                            • Users
                            • Groups
                            • Search
                            • Get Qt Extensions
                            • Unsolved