WebCrypto GOST: Eugene's encrypted Letter

This is a live example. All buttons execute appropriate javascript algorithms with values from the page. For experimentation directly on the page you can change the text or ciphertext, the values ​​of digital signatures, secret keys and passwords, make changes in the javascript algorithms, their settings.

Encryption message for delivery through open channel

Eugene has own generated private key and Tatiana's public key. To encrypt the Letters he needs to create a secret key, which also could easily get Tatiana. He uses derive key function based on asymmetric algorithm for generation keyEncryptionKey with pseudo random userKeyMaterial. When Eugene has written Letter to Tatiana, he generates one-time secret contentEncryptionKey and encrypt message. After Eugene uses generated keyEncryptionKey and userKeyMaterial for wrapping contentEncryptionKey. Wrapped contentEncryptionKey has (Encrypted contentEncryptionKey | keyMAC) with userKeyMaterial will send to Tatiana with encrypted message.

Eugene's Letter to Tatiana
Предвижу все: вас оскорбит
Печальной тайны объясненье.
Какое горькое презренье
Ваш гордый взгляд изобразит!
Чего хочу? с какою целью
Открою душу вам свою?
Какому злобному веселью,
Быть может, повод подаю!
Случайно вас когда-то встретя,
В вас искру нежности заметя,
Я ей поверить не посмел:
Привычке милой не дал ходу;
Свою постылую свободу
Я потерять не захотел.
Еще одно нас разлучило...
Несчастной жертвой Ленский пал...
Ото всего, что сердцу мило,
Тогда я сердце оторвал;
Чужой для всех, ничем не связан,
Я думал: вольность и покой
Замена счастью. Боже мой!
Как я ошибся, как наказан.
Нет, поминутно видеть вас,
Повсюду следовать за вами,
Улыбку уст, движенье глаз
Ловить влюбленными глазами,
Внимать вам долго, понимать
Душой все ваше совершенство,
Пред вами в муках замирать,
Бледнеть и гаснуть... вот блаженство!
И я лишен того: для вас
Тащусь повсюду наудачу;
Мне дорог день, мне дорог час:
А я в напрасной скуке трачу
Судьбой отсчитанные дни.
И так уж тягостны они.
Я знаю: век уж мой измерен;
Но чтоб продлилась жизнь моя,
Я утром должен быть уверен,
Что с вами днем увижусь я...
Боюсь: в мольбе моей смиренной
Увидит ваш суровый взор
Затеи хитрости презренной —
И слышу гневный ваш укор.
Когда б вы знали, как ужасно
Томиться жаждою любви,
Пылать — и разумом всечасно
Смирять волнение в крови;
Желать обнять у вас колени
И, зарыдав, у ваших ног
Излить мольбы, признанья, пени,
Все, все, что выразить бы мог,
А между тем притворным хладом
Вооружать и речь и взор,
Вести спокойный разговор,
Глядеть на вас веселым взглядом!..
Но так и быть: я сам себе
Противиться не в силах боле;
Все решено: я в вашей воле
И предаюсь моей судьбе.
Encrypted by using contentEncryptionKey Eugene's Letter to Tatiana

                    Encrypt channel procedure
                    
// Get private key from secure repository
gostCrypto.subtle.importKey('raw', gostCrypto.coding.Hex.decode(privateEugeneKey.value),
        {name: 'GOSTR3410'}, true, ['deriveKey', 'sign']).then(function(baseKey) {

    // Get peer's public key
    return gostCrypto.subtle.importKey('raw', gostCrypto.coding.Hex.decode(publicTatianaKey.value),
            {name: 'GOSTR3410'}, true, ['deriveKey', 'verify']).then(function(pubKey) {
        var userKeyMaterial = new Uint8Array(8); gostCrypto.getRandomValues(userKeyMaterial);
        ukmKey.value = gostCrypto.coding.Hex.encode(userKeyMaterial);

        // Use peer's public, own private key and seed for create derive key
        return gostCrypto.subtle.deriveKey({name: 'GOSTR3410', hash: {name: 'GOSTR3411'}, ukm: userKeyMaterial,
            public: pubKey}, baseKey, {name: 'GOST28147'}, false, ['wrapKey']).then(function(keyEncryptionKey) {
            
            // Generate contentEncryptionKey (Conten Encryption Key)
            return gostCrypto.subtle.generateKey({name: 'GOST28147'}, false, 
                ['encrypt']).then(function(contentEncryptionKey) {

                // Encrypt message by using contentEncryptionKey. Encrypted message will send 
                // with wrapped contentEncryptionKey
                return gostCrypto.subtle.encrypt({name: 'GOST28147-CFB'}, contentEncryptionKey,
                        gostCrypto.coding.Chars.decode(message.textContent, 'win1251')).then(function(data) {
                    encrypted.textContent = gostCrypto.coding.Base64.encode(data);

                    // Wrap contentEncryptionKey by using keyEncryptionKey and pseudo random UKM 
                    return gostCrypto.subtle.wrapKey('raw', contentEncryptionKey, keyEncryptionKey, 
                        {name: 'GOST28147', ukm: userKeyMaterial});
                }).then(function(encryptedKey) {
                    wrappedKey.value = gostCrypto.coding.Hex.encode(encryptedKey);
                    decryptButton.removeAttribute('disabled');
                });
            });
        });
    });
}).catch(function(error) {
    alert(error.message);
});

Decryption message that delivered through open channel

Tatiana unwrap one-time contentEncryptionKey using own private key and Eugene's public key to derive keyEncryptionKey. Then Tatiana decrypt Letter from Eugene.

Now Tatiana can read decrypted Letter from Eugene. Produced and used keys (keyEncryptionKey, userKeyMaterial and contentEncryptionKey) she burned. She will receive new keys in every packages that contains next messages.

                    

It's not full implementation because message can be changed in the channel. Use Digital signatures as in previous examples for ensure data immutability.

Decrypt channel procedure
// Get private key from secluded place
gostCrypto.subtle.importKey('raw', gostCrypto.coding.Hex.decode(privateTatianaKey.value),
        {name: 'GOSTR3410'}, true, ['deriveKey', 'sign']).then(function(baseKey) {

    // Get peer's public key
    return gostCrypto.subtle.importKey('raw', gostCrypto.coding.Hex.decode(publicEugeneKey.value),
            {name: 'GOSTR3410'}, true, ['deriveKey', 'verify']).then(function(pubKey) {
        
        // Get userKeyMaterial 
        var encryptedKey = gostCrypto.coding.Hex.decode(wrappedKey.value); 
            userKeyMaterial = gostCrypto.coding.Hex.decode(ukmKey.value);

        // Use peer's public, own private key and seed for create derive key
        return gostCrypto.subtle.deriveKey({name: 'GOSTR3410', hash: {name: 'GOSTR3411'}, ukm: userKeyMaterial,
            public: pubKey}, baseKey, {name: 'GOST28147'}, true, ['unwrapKey']).then(function(keyEncryptionKey) {

            // Unwrap contentEncryptionKey using keyEncryptionKey
            return gostCrypto.subtle.unwrapKey('raw', encryptedKey, keyEncryptionKey, 
                {name: 'GOST28147', ukm: userKeyMaterial}, {name: 'GOST28147'}, false, ['decrypt']);
        }).then(function(contentEncryptionKey) {

            // Decrypt message
            return gostCrypto.subtle.decrypt({name: 'GOST28147-CFB'}, contentEncryptionKey,
                    gostCrypto.coding.Base64.decode(encrypted.textContent));
        }).then(function(data) {
            decrypted.textContent = gostCrypto.coding.Chars.encode(data, 'win1251');
        });
    });
}).catch(function(error) {
    alert(error.message);
});