Last active
December 28, 2020 20:07
-
-
Save le0pard/3a18a3cac4cf636ffa096f4011082869 to your computer and use it in GitHub Desktop.
AES-256-GCM on Ruby and JS (Web Crypto API and forge.js)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// you need to use https://github.com/digitalbazaar/forge | |
const encryptData = (key, data) => { | |
const mdKey = forge.md.sha256.create() | |
mdKey.update(key) | |
const iv = forge.random.getBytesSync(12) | |
const cipher = forge.cipher.createCipher('AES-GCM', mdKey.digest()) | |
cipher.start({ | |
iv: iv, | |
additionalData: '', | |
tagLength: 128 | |
}); | |
cipher.update(forge.util.createBuffer(data)) | |
cipher.finish() | |
const encrypted = cipher.output | |
const tag = cipher.mode.tag | |
return `v1--${forge.util.encode64(encrypted.getBytes())}--${forge.util.encode64(iv)}--${forge.util.encode64(tag.getBytes())}` | |
} | |
const decryptData = (key, data) => { | |
const mdKey = forge.md.sha256.create() | |
mdKey.update(key) | |
const [version, encoded, iv, authTag] = data.split('--') | |
const decipher = forge.cipher.createDecipher('AES-GCM', mdKey.digest()) | |
decipher.start({ | |
iv: forge.util.createBuffer(forge.util.decode64(iv)), | |
additionalData: '', // optional | |
tagLength: 128, // optional, defaults to 128 bits | |
tag: forge.util.createBuffer(forge.util.decode64(authTag)) // authentication tag from encryption | |
}) | |
decipher.update(forge.util.createBuffer(forge.util.decode64(encoded))) | |
var pass = decipher.finish() | |
// pass is false if there was a failure (eg: authentication tag didn't match) | |
if (pass) { | |
// outputs decrypted hex | |
return decipher.output.data | |
} else { | |
throw new Error('invalid decode') | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'openssl' | |
require 'base64' | |
def aes256_encrypt(key, data) | |
key = Digest::SHA256.digest(key) | |
cipher = OpenSSL::Cipher.new('AES-256-GCM') | |
iv = cipher.random_iv | |
cipher.encrypt | |
cipher.iv = iv | |
cipher.key = key | |
cipher.auth_data = '' | |
encrypted_data = cipher.update(data) | |
encrypted_data << cipher.final | |
"v1--#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}--#{::Base64.strict_encode64 cipher.auth_tag}" | |
end | |
def aes256_decrypt(key, data) | |
key = Digest::SHA256.digest(key) | |
cipher = OpenSSL::Cipher.new('AES-256-GCM') | |
version, encrypted_data, iv, auth_tag = data.split("--".freeze) | |
cipher.decrypt | |
cipher.key = key | |
cipher.iv = ::Base64.strict_decode64(iv) | |
cipher.auth_tag = ::Base64.strict_decode64(auth_tag) | |
cipher.auth_data = '' | |
decrypted_data = cipher.update(::Base64.strict_decode64(encrypted_data)) | |
decrypted_data << cipher.final | |
decrypted_data | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const bufferToBase64 = (buffer) => { | |
let bytes = new Uint8Array(buffer); | |
let binary = ""; | |
let len = bytes.byteLength; | |
for (let i = 0; i < len; i++) { | |
binary += String.fromCharCode(bytes[i]); | |
} | |
return window.btoa(binary); | |
} | |
const base64ToBuffer = (base64) => { | |
let binary_string = window.atob(base64); | |
let len = binary_string.length; | |
let bytes = new Uint8Array(len); | |
for (var i = 0; i < len; i++) { | |
bytes[i] = binary_string.charCodeAt(i); | |
} | |
return bytes.buffer; | |
} | |
const encryptData = async (key, data) => { | |
const encoder = new TextEncoder('utf-8') | |
const keyData = encoder.encode(key) | |
key = await crypto.subtle.digest('SHA-256', keyData) | |
const resKey = await window.crypto.subtle.importKey( | |
"raw", | |
key, | |
{ | |
name: "AES-GCM", | |
}, | |
false, | |
["encrypt", "decrypt"] | |
) | |
const iv = window.crypto.getRandomValues(new Uint8Array(12)) | |
const enc = new TextEncoder('utf-8') | |
const encoded = enc.encode(data) | |
const additionalDataEnc = new TextEncoder('utf-8') | |
const additionalData = additionalDataEnc.encode('') | |
const tagLength = 128 | |
const dataAndAuthTag = await window.crypto.subtle.encrypt( | |
{ | |
name: "AES-GCM", | |
iv: iv, | |
additionalData: additionalData, | |
tagLength: tagLength | |
}, | |
resKey, | |
encoded | |
) | |
const encryptedData = dataAndAuthTag.slice(0, dataAndAuthTag.byteLength - ((tagLength + 7) >> 3)) | |
const authTag = dataAndAuthTag.slice(dataAndAuthTag.byteLength - ((tagLength + 7) >> 3)) | |
return `v1--${bufferToBase64(encryptedData)}--${bufferToBase64(iv)}--${bufferToBase64(authTag)}` | |
} | |
const decryptData = async (key, data) => { | |
const encoder = new TextEncoder('utf-8') | |
const keyData = encoder.encode(key) | |
key = await crypto.subtle.digest('SHA-256', keyData) | |
const resKey = await window.crypto.subtle.importKey( | |
"raw", | |
key, | |
{ | |
name: "AES-GCM", | |
}, | |
false, | |
["encrypt", "decrypt"] | |
) | |
const [version, ...base64Data] = data.split('--') | |
const [encoded, iv, authTag] = base64Data.map((i) => base64ToBuffer(i)) | |
const dataAndAuthTag = new Uint8Array(encoded.byteLength + authTag.byteLength) | |
dataAndAuthTag.set(new Uint8Array(encoded), 0) | |
dataAndAuthTag.set(new Uint8Array(authTag), encoded.byteLength) | |
const additionalDataEnc = new TextEncoder('utf-8') | |
const additionalData = additionalDataEnc.encode('') | |
const decrypted = await window.crypto.subtle.decrypt( | |
{ | |
name: "AES-GCM", | |
iv: iv, | |
additionalData: additionalData, | |
tagLength: 128 | |
}, | |
resKey, | |
dataAndAuthTag | |
) | |
return new TextDecoder("utf8").decode(decrypted) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment