mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-17 11:15:12 -04:00
Add tiered data cards:
- Tier 1: Basic data card operations
- Tier 2: Basic operations + AES-CBC encryption/decryption
- Tier 3: Basic + AES-CBC + ECDSA + ECDH
- Textures are now looking like a stubs that are waiting for better alternatives
- Recipes by Kubuxu
- gpg.lua program (simple version without keychain)
--- Change log after squash ---
+ Changed to ThreadLocal instance of SecureRandom instead of genSeed function
+ Added readable errors in case of invalid key type
+ Added SecureRandom instance to EC key generator to avoid unnecessary creation of another one.
+ Added check that data card is present and `data.present()` function
+ Fixed typo at 029915d595/src/main/resources/assets/opencomputers/lua/component/data/lib/data.lua (L15)
This commit is contained in:
parent
cdcbcc44b8
commit
3f69cab11a
@ -182,7 +182,10 @@ dependencies {
|
||||
provided "codechicken:WR-CBE:${config.minecraft.version}-${config.wrcbe.version}:dev"
|
||||
provided "com.bluepowermod:BluePower:${config.bluepower.version}:deobf"
|
||||
provided "com.gregoriust.gregtech:gregtech_${config.minecraft.version}:${config.gt.version}:dev"
|
||||
provided "dev.calclavia.resonantengine:resonant-engine:${config.re.version}:dev"
|
||||
|
||||
// Fails to download and OC doesn't seems to have integration module for it.
|
||||
//provided "dev.calclavia.resonantengine:resonant-engine:${config.re.version}:dev"
|
||||
|
||||
provided "igwmod:IGW-Mod-1.7.10:${config.igwmod.version}:userdev"
|
||||
provided "mcp.mobius.waila:Waila:${config.waila.version}_${config.minecraft.version}:dev"
|
||||
provided "net.industrial-craft:industrialcraft-2:${config.ic2.version}:dev"
|
||||
|
@ -747,11 +747,28 @@ opencomputers {
|
||||
# moving very slowly this also does not trigger.
|
||||
hoverBootMove: 1
|
||||
|
||||
# Cost for simple operations on the data card, such as hashing.
|
||||
dataCardSimple: 0.25
|
||||
# Cost for trivial operations on the data card, such as CRC32 or Base64
|
||||
dataCardTrivial: 0.2
|
||||
|
||||
# Cost for complex operations on the data card, such as inflate/deflate.
|
||||
dataCardComplex: 4.0
|
||||
# Per-byte cost for trivial operations
|
||||
dataCardTrivialByte: 0.005
|
||||
|
||||
# Cost for simple operations on the data card, such as MD5 or AES
|
||||
dataCardSimple: 1.0
|
||||
|
||||
# Per-byte cost for simple operations
|
||||
dataCardSimpleByte: 0.01
|
||||
|
||||
# Cost for complex operations on the data card, such as SHA256, inflate/deflate and SecureRandom.
|
||||
dataCardComplex: 6.0
|
||||
|
||||
# Per-byte cost for complex operations
|
||||
dataCardComplexByte: 0.1
|
||||
|
||||
# Cost for asymmetric operations on the data card, such as ECDH and ECDSA
|
||||
# Per-byte cost for ECDSA operation is controlled by `complex` value,
|
||||
# because data is hashed with SHA256 before signing/verifying
|
||||
dataCardAsymmetric: 10.0
|
||||
}
|
||||
|
||||
# The rate at which different blocks accept external power. All of these
|
||||
|
@ -1,7 +1,12 @@
|
||||
# Карта данных
|
||||
|
||||

|
||||
|
||||
Карта данных предоставляет несколько алгоритмов, которые трудно реализуемы в ОК или работают гораздо медленнее, чем должны. Представлены алгоритмы хеширования, а также упаковки/распаковки. Дополнительно, карта имеет свою файловую систему, в которой есть несколько программ, реализующих данные алгоритмы, также как у интернет карты.
|
||||
|
||||
Не забывайте, что существуют ограничения на размер данных, которые могут быть одновременно обработаны, а также, что выполнение каждой операции тратит энергию.
|
||||
# Карта данных
|
||||
|
||||

|
||||
|
||||
Карта данных предоставляет несколько алгоритмов, которые трудно реализуемы в ОК или работают гораздо медленнее, чем должны. Дополнительно, карта имеет свою файловую систему, в которой есть несколько программ, реализующих данные алгоритмы, также как у интернет карты.
|
||||
|
||||
Не забывайте, что существуют ограничения на размер данных, которые могут быть одновременно обработаны, а также, что выполнение каждой операции тратит энергию.
|
||||
|
||||
Карта имеет три уровня:
|
||||
* **Первый уровень**: поддерживает кодирование и декодирование Base64, CRC32, MD5, SHA256, сжатие и распаковку данных алгоритмом Deflate
|
||||
* **Второй уровень**: MD5 и SHA256 могут работать в режиме HMAC, генерация случайных данных, шифрование и расшифровка AES
|
||||
* **Третий уровень**: ECDSA и ECDH - генерация ключей, подпись данных, выработка сессионных ключей
|
@ -59,7 +59,9 @@ item.oc.CPU0.name=Hauptprozessor (CPU) (Stufe 1)
|
||||
item.oc.CPU1.name=Hauptprozessor (CPU) (Stufe 2)
|
||||
item.oc.CPU2.name=Hauptprozessor (CPU) (Stufe 3)
|
||||
item.oc.CuttingWire.name=Schneidedraht
|
||||
item.oc.DataCard.name=Datenkarte
|
||||
item.oc.DataCard0.name=Datenkarte (Stufe 1)
|
||||
item.oc.DataCard1.name=Datenkarte (Stufe 2)
|
||||
item.oc.DataCard2.name=Datenkarte (Stufe 3)
|
||||
item.oc.DebugCard.name=Debug-Karte
|
||||
item.oc.Debugger.name=Netzwerk-Debugger
|
||||
item.oc.Disk.name=Platte
|
||||
@ -259,7 +261,9 @@ oc:tooltip.ComponentBus=Diese Erweiterung erlaubt es es Servern, mit noch mehr K
|
||||
oc:tooltip.CPU=Kernstück eines jeden Computers. Die Taktrate hat einen leichten Schatten, aber was kann man von einer Taschensonnenuhr schon erwarten?[nl] Unterstützte Komponenten: §f%s§7
|
||||
oc:tooltip.CPU.Architecture=Architektur: §f%s§7
|
||||
oc:tooltip.CuttingWire=Wird gebraucht, um Tonblöcke in Leiterplattenform zu bekommen. Vermutlich das ineffizienteste Werkzeug in der Geschichte der Menschheit, da es nach einer Verwendung kaputt geht.
|
||||
oc:tooltip.DataCard=Stellt einige komplexe Algorithmen wie Hash-Funktionen und deflate/inflate bereit.
|
||||
oc:tooltip.DataCard0=Stellt einige komplexe Algorithmen wie Hash-Funktionen und deflate/inflate bereit.
|
||||
oc:tooltip.DataCard1=Stellt einige komplexe Algorithmen wie Hash-Funktionen und deflate/inflate bereit.
|
||||
oc:tooltip.DataCard2=Stellt einige komplexe Algorithmen wie Hash-Funktionen und deflate/inflate bereit.
|
||||
oc:tooltip.DebugCard=Kreativ-Modus-Gegenstand, erlaubt es die Welt zu manipulieren um das Testen zu erleichtern. Verwendung auf eigene Gefahr.
|
||||
oc:tooltip.Debugger=Erlaubt, Informationen über OCs internes Netzwerk auszugeben. Nur verwenden, wenn von einem Entwickler dazu aufgefordert.
|
||||
oc:tooltip.Disassembler=Zerlegt Gegenstände in ihre Einzelteile. §lWarnung§7: zurückgewonnene Gegenstände haben eine %s%%-ige Chance beim Extrahieren kaputt zu gehen!
|
||||
|
@ -60,7 +60,9 @@ item.oc.CPU0.name=Central Processing Unit (CPU) (Tier 1)
|
||||
item.oc.CPU1.name=Central Processing Unit (CPU) (Tier 2)
|
||||
item.oc.CPU2.name=Central Processing Unit (CPU) (Tier 3)
|
||||
item.oc.CuttingWire.name=Cutting Wire
|
||||
item.oc.DataCard.name=Data Card
|
||||
item.oc.DataCard0.name=Data Card (Tier 1)
|
||||
item.oc.DataCard1.name=Data Card (Tier 2)
|
||||
item.oc.DataCard2.name=Data Card (Tier 3)
|
||||
item.oc.DebugCard.name=Debug Card
|
||||
item.oc.Debugger.name=Network Debugger
|
||||
item.oc.Disk.name=Disk Platter
|
||||
@ -261,7 +263,9 @@ oc:tooltip.ComponentBus=This expansion allows servers to communicate with more c
|
||||
oc:tooltip.CPU=An essential component of all computers. The clock rate is a bit unreliable, but what do you expect when it runs on a pocket sundial?[nl] Supported components: §f%s§7
|
||||
oc:tooltip.CPU.Architecture=Architecture: §f%s§7
|
||||
oc:tooltip.CuttingWire=Used to cut clay blocks into circuit board shape. Breaks after one use, which probably makes it the most inefficient tool ever.
|
||||
oc:tooltip.DataCard=Provides a couple of advanced algorithms such as hashing and deflate/inflate.
|
||||
oc:tooltip.DataCard0=Provides a couple of advanced algorithms such as hashing and deflate/inflate.
|
||||
oc:tooltip.DataCard1=Provides a couple of advanced algorithms such as hashing, AES encryption and deflate/inflate.
|
||||
oc:tooltip.DataCard2=Provides a couple of advanced algorithms such as hashing, AES encryption, elliptic curve cryptography and deflate/inflate.
|
||||
oc:tooltip.DebugCard=Creative mode item, allows manipulating the world to make testing easier. Use at your own peril.
|
||||
oc:tooltip.Debugger=Can be used to output debug information on OC's internal network grid. Only use if so instructed by a dev.
|
||||
oc:tooltip.Disassembler=Separates items into their original components. §lWarning§7: returned items have a %s%% chance of breaking in the process!
|
||||
|
@ -260,7 +260,9 @@ oc:tooltip.ComponentBus=Позволяет серверам взаимодейс
|
||||
oc:tooltip.CPU=Является важным компонентом в компьютере. Тактовая частота немного нестабильна, но что вы ожидали, ведь он работает даже на карманных солнечных часах. Количество поддерживаемых компонентов: §f%s§7
|
||||
oc:tooltip.CPU.Architecture=Архитектура: §f%s§7
|
||||
oc:tooltip.CuttingWire=Используется для нарезки глиняных блоков в пластины. Рвётся после использования, что делает её очень неэффективным инструментом.
|
||||
oc:tooltip.DataCard=Обеспечивает поддержку нескольких алгоритмов хеширования, а также сжатия.
|
||||
oc:tooltip.DataCard0=Обеспечивает поддержку нескольких алгоритмов хеширования, а также сжатия.
|
||||
oc:tooltip.DataCard1=Обеспечивает поддержку шифрования, нескольких алгоритмов хеширования, а также сжатия.
|
||||
oc:tooltip.DataCard2=Обеспечивает поддержку шифрования и эллиптической криптографии, нескольких алгоритмов хеширования, а также сжатия.
|
||||
oc:tooltip.DebugCard=Креативный предмет, позволяет манипулировать игровым миром. Используйте на свой страх и риск.
|
||||
oc:tooltip.Debugger=Может быть использован для вывода отладочной информации о внутренней сети.
|
||||
oc:tooltip.Disassembler=Разделяет предметы на исходные компоненты. §lВнимание§7: возвращённые предметы имеют шанс %s%% сломаться!
|
||||
|
@ -0,0 +1,241 @@
|
||||
--[[
|
||||
-- A program that allows user to perform all crypto operations provided by Tier II / Tier III data cards
|
||||
-- Author: makkarpov
|
||||
--]]
|
||||
|
||||
local shell = require("shell")
|
||||
local data = require("data")
|
||||
local term = require("term")
|
||||
local filesystem = require("filesystem")
|
||||
local serialization = require("serialization")
|
||||
|
||||
local args, options = shell.parse(...)
|
||||
|
||||
local function writeFile(path, data)
|
||||
if filesystem.exists(path) then
|
||||
print("gpg: failed to write file: " .. path)
|
||||
print("gpg: error was: file already exists")
|
||||
return false
|
||||
end
|
||||
|
||||
if type(data) == "table" then
|
||||
data = serialization.serialize(data)
|
||||
end
|
||||
|
||||
local h, err = io.open(path, "wb")
|
||||
|
||||
if not h then
|
||||
print("gpg: failed to write file: " .. path)
|
||||
print("gpg: error was: " .. err)
|
||||
return false
|
||||
end
|
||||
|
||||
h:write(data)
|
||||
h:close()
|
||||
return true
|
||||
end
|
||||
|
||||
local function readFile(path, deserialize)
|
||||
local h = io.open(path, "rb")
|
||||
local r = h:read("*a")
|
||||
h:close()
|
||||
|
||||
if deserialize then
|
||||
r = serialization.unserialize(r)
|
||||
end
|
||||
|
||||
return r
|
||||
end
|
||||
|
||||
local function parseKey(path, isPublic)
|
||||
local d = readFile(path, true)
|
||||
local k, err = data.deserializeKey(d.d, d.t)
|
||||
|
||||
if not k then
|
||||
print("gpg: failed to parse key: " .. err)
|
||||
return nil
|
||||
end
|
||||
|
||||
if k.isPublic() ~= isPublic then
|
||||
print("gpg: wrong key type")
|
||||
return nil
|
||||
end
|
||||
|
||||
return k
|
||||
end
|
||||
|
||||
local function deriveName(base, encrypt)
|
||||
if encrypt then
|
||||
return base .. ".gpg"
|
||||
else
|
||||
local d = base:gsub(".gpg", "")
|
||||
if d == base then
|
||||
d = d .. ".dec"
|
||||
print("gpg: decrypting to " .. d)
|
||||
end
|
||||
return d
|
||||
end
|
||||
end
|
||||
|
||||
local function ensureTier(tier)
|
||||
if not require("component").isAvailable("data") then
|
||||
print("gpg: you must have data card in order to run this program")
|
||||
error("data card is absent")
|
||||
end
|
||||
|
||||
if data.tier() < tier then
|
||||
print("gpg: you must have tier " .. tier .. " data card in order to run this program")
|
||||
error("data card is too simple")
|
||||
end
|
||||
end
|
||||
|
||||
if options['g'] and (#args == 2) then
|
||||
ensureTier(3)
|
||||
local pub, priv = data.generateKeyPair(384)
|
||||
|
||||
priv = { t = priv.keyType(), d = priv.serialize() }
|
||||
pub = { t = pub.keyType(), d = pub.serialize() }
|
||||
|
||||
if not writeFile(args[1], priv) then
|
||||
print("gpg: failed to write private key, aborting")
|
||||
return false
|
||||
end
|
||||
|
||||
if not writeFile(args[2], pub) then
|
||||
print("gpg: failed to write public key, aborting")
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if options['c'] and (options['e'] or options['d']) and (#args == 1) then
|
||||
ensureTier(2)
|
||||
if options['d'] and options['e'] then
|
||||
print("gpg: please specify either -d or -e")
|
||||
return false
|
||||
end
|
||||
|
||||
io.write("gpg: enter password: ")
|
||||
local aesKey = data.md5(term.read(nil, nil, nil, "*"))
|
||||
local checkValue = data.sha256(aesKey)
|
||||
|
||||
if options['e'] then
|
||||
local iv = data.random(16)
|
||||
local d = data.encrypt(readFile(args[1]), aesKey, iv)
|
||||
|
||||
return writeFile(deriveName(args[1], true), {
|
||||
t = "pwd",
|
||||
kdf = "md5",
|
||||
iv = iv,
|
||||
cv = checkValue,
|
||||
d = d
|
||||
})
|
||||
else
|
||||
local d = readFile(args[1], true)
|
||||
|
||||
if d.t ~= "pwd" then
|
||||
print("gpg: file is not encrypted with a password")
|
||||
return false
|
||||
end
|
||||
|
||||
if checkValue ~= d.cv then
|
||||
print("gpg: password incorrect")
|
||||
return false
|
||||
end
|
||||
|
||||
return writeFile(deriveName(args[1], false), data.decrypt(d.d, aesKey, d.iv))
|
||||
end
|
||||
end
|
||||
|
||||
if (options['d'] or options['e']) and (#args == 2) then
|
||||
ensureTier(3)
|
||||
if options['d'] and options['e'] then
|
||||
print("gpg: please specify either -d or -e")
|
||||
return false
|
||||
end
|
||||
|
||||
if options['e'] then
|
||||
local userPub = parseKey(args[1], true)
|
||||
local tmpPub, tmpPriv = data.generateKeyPair(384)
|
||||
local aesKey = data.md5(data.ecdh(tmpPriv, userPub))
|
||||
local checkValue = data.sha256(aesKey)
|
||||
local iv = data.random(16)
|
||||
|
||||
local d = data.encrypt(readFile(args[2]), aesKey, iv)
|
||||
return writeFile(deriveName(args[2], true), {
|
||||
t = "ecdh",
|
||||
kdf = "md5",
|
||||
iv = iv,
|
||||
cv = checkValue,
|
||||
k = {
|
||||
t = tmpPub.keyType(),
|
||||
d = tmpPub.serialize()
|
||||
},
|
||||
d = d
|
||||
})
|
||||
else
|
||||
local userPriv = parseKey(args[1], false)
|
||||
local d = readFile(args[2], true)
|
||||
|
||||
if d.t ~= "ecdh" then
|
||||
print("gpg: file is not encrypted with a key")
|
||||
return false
|
||||
end
|
||||
|
||||
local tmpPub = data.deserializeKey(d.k.d, d.k.t)
|
||||
local aesKey = data.md5(data.ecdh(userPriv, tmpPub))
|
||||
|
||||
if d.cv ~= data.sha256(aesKey) then
|
||||
print("gpg: invalid key")
|
||||
return false
|
||||
end
|
||||
|
||||
return writeFile(deriveName(args[2], false), data.decrypt(d.d, aesKey, d.iv))
|
||||
end
|
||||
end
|
||||
|
||||
if (options['s'] or options['v']) and (#args == 2) then
|
||||
ensureTier(2)
|
||||
if options['s'] and options['v'] then
|
||||
print("gpg: please specify either -s or -v")
|
||||
return false
|
||||
end
|
||||
|
||||
if options['s'] then
|
||||
local userPriv = parseKey(args[1], false)
|
||||
local sign = data.ecdsa(readFile(args[2]), userPriv)
|
||||
|
||||
return writeFile(args[2] .. ".sig", {
|
||||
t = "ecdsa",
|
||||
s = sign
|
||||
})
|
||||
else
|
||||
local userPub = parseKey(args[1], true)
|
||||
local sign = readFile(args[2] .. ".sig", true)
|
||||
|
||||
if sign.t ~= "ecdsa" then
|
||||
print("gpg: unsupported signature type")
|
||||
return false
|
||||
end
|
||||
|
||||
if not data.ecdsa(readFile(args[2]), userPub, sign.s) then
|
||||
print("gpg: signature verification failed")
|
||||
return false
|
||||
end
|
||||
|
||||
print("gpg: signature is valid")
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
io.write("Usages:\n")
|
||||
io.write("gpg -ce <file> -- encrypt file with password\n")
|
||||
io.write("gpg -cd <file> -- decrypt file with password\n")
|
||||
io.write("gpg -e <key> <file> -- encrypt file\n")
|
||||
io.write("gpg -d <key> <file> -- decrypt file\n")
|
||||
io.write("gpg -g <private key file> <public key file> -- generate keypair\n")
|
||||
io.write("gpg -s <key> <file> -- sign file\n")
|
||||
io.write("gpg -v <key> <file> -- verify file\n")
|
||||
return false
|
@ -12,45 +12,27 @@ end
|
||||
|
||||
-- Converts hexadecimal string into binary data.
|
||||
function data.fromHex(hex)
|
||||
return (data:gsub('..', function (cc)
|
||||
return (hex:gsub('..', function (cc)
|
||||
return string.char(tonumber(cc, 16))
|
||||
end))
|
||||
end
|
||||
|
||||
-- Applies base64 encoding.
|
||||
function data.encode64(data)
|
||||
return component.data.encode64(data)
|
||||
end
|
||||
if component.isAvailable("data") then
|
||||
local wrappedFunctions = { 'encode64', 'decode64', 'sha256', 'md5', 'crc32', 'deflate', 'inflate',
|
||||
'getLimit', 'tier', 'encrypt', 'decrypt', 'random', 'generateKeyPair',
|
||||
'deserializeKey', 'ecdh', 'ecdsa' }
|
||||
|
||||
-- Applies base64 decoding.
|
||||
function data.decode64(data)
|
||||
return component.data.decode64(data)
|
||||
end
|
||||
function data.present()
|
||||
return true
|
||||
end
|
||||
|
||||
-- Returns raw/binary SHA2-256 hash of data. Common form of presenting SHA is hexadecimal string, see data.toHex.
|
||||
function data.sha256(data)
|
||||
return component.data.sha256(data)
|
||||
end
|
||||
|
||||
-- Returns raw/binary MD5 hash of data. Common form of presenting SHA is hexadecimal string, see data.toHex.
|
||||
function data.md5(data)
|
||||
return component.data.md5(data)
|
||||
end
|
||||
|
||||
-- Returns raw/binary CRC-32 hash of data. Common form of presenting SHA is hexadecimal string, see data.toHex.
|
||||
function data.crc32(data)
|
||||
return component.data.crc32(data)
|
||||
end
|
||||
|
||||
|
||||
-- Applies DEFLATE compression.
|
||||
function data.deflate(data)
|
||||
return component.data.deflate(data)
|
||||
end
|
||||
|
||||
-- Applies INFLATE decompression.
|
||||
function data.inflate(data)
|
||||
return component.data.inflate(data)
|
||||
for i, v in ipairs(wrappedFunctions) do
|
||||
data[v] = component.data[v]
|
||||
end
|
||||
else
|
||||
function data.present()
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
|
@ -149,6 +149,14 @@ dataCard {
|
||||
input: [[nuggetIron, "oc:materialALU", "oc:circuitChip2"]
|
||||
["", "oc:materialCard", ""]]
|
||||
}
|
||||
dataCard2 {
|
||||
input: [[nuggetGold, "oc:materialALU", "oc:circuitChip3"]
|
||||
["", "oc:dataCard", ""]]
|
||||
}
|
||||
dataCard3 {
|
||||
input: [[diamond, "oc:cpu2", "oc:circuitChip3"]
|
||||
["", "oc:dataCard2", ""]]
|
||||
}
|
||||
graphicsCard1 {
|
||||
input: [["oc:circuitChip1", "oc:materialALU", "oc:ram1"]
|
||||
["", "oc:materialCard", ""]]
|
||||
|
Before Width: | Height: | Size: 489 B After Width: | Height: | Size: 489 B |
Binary file not shown.
After Width: | Height: | Size: 749 B |
@ -0,0 +1,15 @@
|
||||
{
|
||||
"animation": {
|
||||
"frametime": 1,
|
||||
"frames": [
|
||||
{ "index": 0, "time": 4 },
|
||||
{ "index": 1, "time": 4 },
|
||||
{ "index": 2, "time": 4 },
|
||||
{ "index": 3, "time": 4 },
|
||||
{ "index": 4, "time": 4 },
|
||||
{ "index": 5, "time": 4 },
|
||||
{ "index": 6, "time": 4 },
|
||||
{ "index": 7, "time": 4 }
|
||||
]
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 710 B |
@ -0,0 +1,15 @@
|
||||
{
|
||||
"animation": {
|
||||
"frametime": 1,
|
||||
"frames": [
|
||||
{ "index": 0, "time": 4 },
|
||||
{ "index": 1, "time": 4 },
|
||||
{ "index": 2, "time": 4 },
|
||||
{ "index": 3, "time": 4 },
|
||||
{ "index": 4, "time": 4 },
|
||||
{ "index": 5, "time": 4 },
|
||||
{ "index": 6, "time": 4 },
|
||||
{ "index": 7, "time": 4 }
|
||||
]
|
||||
}
|
||||
}
|
@ -80,7 +80,9 @@ object Constants {
|
||||
final val DatabaseUpgradeTier1 = "databaseUpgrade1"
|
||||
final val DatabaseUpgradeTier2 = "databaseUpgrade2"
|
||||
final val DatabaseUpgradeTier3 = "databaseUpgrade3"
|
||||
final val DataCard = "dataCard"
|
||||
final val DataCardTier1 = "dataCard"
|
||||
final val DataCardTier2 = "dataCard2"
|
||||
final val DataCardTier3 = "dataCard3"
|
||||
final val DebugCard = "debugCard"
|
||||
final val Debugger = "debugger"
|
||||
final val Disk = "disk"
|
||||
|
@ -193,8 +193,13 @@ class Settings(val config: Config) {
|
||||
val hoverBootJump = config.getDouble("power.cost.hoverBootJump") max 0
|
||||
val hoverBootAbsorb = config.getDouble("power.cost.hoverBootAbsorb") max 0
|
||||
val hoverBootMove = config.getDouble("power.cost.hoverBootMove") max 0
|
||||
val dataCardTrivial = config.getDouble("power.cost.dataCardTrivial") max 0
|
||||
val dataCardTrivialByte = config.getDouble("power.cost.dataCardTrivialByte") max 0
|
||||
val dataCardSimple = config.getDouble("power.cost.dataCardSimple") max 0
|
||||
val dataCardSimpleByte = config.getDouble("power.cost.dataCardSimpleByte") max 0
|
||||
val dataCardComplex = config.getDouble("power.cost.dataCardComplex") max 0
|
||||
val dataCardComplexByte = config.getDouble("power.cost.dataCardComplexByte") max 0
|
||||
val dataCardAsymmetric = config.getDouble("power.cost.dataCardAsymmetric") max 0
|
||||
|
||||
// power.rate
|
||||
val accessPointRate = config.getDouble("power.rate.accessPoint") max 0
|
||||
|
@ -536,6 +536,10 @@ object Items extends ItemAPI {
|
||||
registerItem(new item.APU(multi, Tier.Three), Constants.ItemName.APUCreative)
|
||||
|
||||
// 1.5.13
|
||||
Recipes.addSubItem(new item.DataCard(multi), Constants.ItemName.DataCard, "oc:dataCard")
|
||||
Recipes.addSubItem(new item.DataCard(multi, Tier.One), Constants.ItemName.DataCardTier1, "oc:dataCard")
|
||||
|
||||
// ?.?.?
|
||||
Recipes.addSubItem(new item.DataCard(multi, Tier.Two), Constants.ItemName.DataCardTier2, "oc:dataCard2")
|
||||
Recipes.addSubItem(new item.DataCard(multi, Tier.Three), Constants.ItemName.DataCardTier3, "oc:dataCard3")
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
package li.cil.oc.common.item
|
||||
|
||||
class DataCard(val parent: Delegator) extends traits.Delegate
|
||||
class DataCard(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier {
|
||||
override val unlocalizedName = super.unlocalizedName + tier
|
||||
|
||||
override protected def tooltipName = Option(super.unlocalizedName + tier)
|
||||
}
|
@ -2,19 +2,31 @@ package li.cil.oc.integration.opencomputers
|
||||
|
||||
import li.cil.oc.api.driver.EnvironmentAware
|
||||
import li.cil.oc.api.driver.EnvironmentHost
|
||||
import li.cil.oc.common.Slot
|
||||
import li.cil.oc.common.{Tier, Slot}
|
||||
import li.cil.oc.common.item.Delegator
|
||||
import li.cil.oc.server.component
|
||||
import li.cil.oc.Constants
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.{common, Constants, api}
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
object DriverDataCard extends Item with EnvironmentAware {
|
||||
override def worksWith(stack: ItemStack) = isOneOf(stack,
|
||||
api.Items.get(Constants.ItemName.DataCard))
|
||||
api.Items.get(Constants.ItemName.DataCardTier1),
|
||||
api.Items.get(Constants.ItemName.DataCardTier2),
|
||||
api.Items.get(Constants.ItemName.DataCardTier3))
|
||||
|
||||
override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = new component.DataCard()
|
||||
override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = tier(stack) match {
|
||||
case Tier.One => new component.DataCard.Tier1()
|
||||
case Tier.Two => new component.DataCard.Tier2()
|
||||
case Tier.Three => new component.DataCard.Tier3()
|
||||
}
|
||||
|
||||
override def slot(stack: ItemStack) = Slot.Card
|
||||
|
||||
override def tier(stack: ItemStack) =
|
||||
Delegator.subItem(stack) match {
|
||||
case Some(data: common.item.DataCard) => data.tier
|
||||
case _ => Tier.One
|
||||
}
|
||||
|
||||
override def providedEnvironment(stack: ItemStack) = classOf[component.DataCard]
|
||||
}
|
||||
|
@ -1,19 +1,17 @@
|
||||
package li.cil.oc.server.component
|
||||
|
||||
import java.util.zip.DeflaterOutputStream
|
||||
import java.util.zip.InflaterOutputStream
|
||||
import java.security._
|
||||
import java.security.interfaces.{ECPrivateKey, ECPublicKey}
|
||||
import java.security.spec.{PKCS8EncodedKeySpec, X509EncodedKeySpec}
|
||||
import java.util.zip.{DeflaterOutputStream, InflaterOutputStream}
|
||||
import javax.crypto.spec.{IvParameterSpec, SecretKeySpec}
|
||||
import javax.crypto.{Cipher, KeyAgreement, Mac}
|
||||
|
||||
import com.google.common.hash.Hashing
|
||||
import li.cil.oc.OpenComputers
|
||||
import li.cil.oc.Settings
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.api.Network
|
||||
import li.cil.oc.api.machine.Arguments
|
||||
import li.cil.oc.api.machine.Callback
|
||||
import li.cil.oc.api.machine.Context
|
||||
import li.cil.oc.api.network.Node
|
||||
import li.cil.oc.api.network.Visibility
|
||||
import li.cil.oc.api.prefab
|
||||
import li.cil.oc.{OpenComputers, Settings, api}
|
||||
import li.cil.oc.api.machine.{Arguments, Callback, Context}
|
||||
import li.cil.oc.api.network.{Node, Visibility}
|
||||
import li.cil.oc.api.{Network, prefab}
|
||||
import li.cil.oc.util.ExtendedNBT._
|
||||
import net.minecraft.nbt.NBTTagCompound
|
||||
import org.apache.commons.codec.binary.Base64
|
||||
@ -28,67 +26,30 @@ class DataCard extends prefab.ManagedEnvironment {
|
||||
val romData = Option(api.FileSystem.asManagedEnvironment(api.FileSystem.
|
||||
fromClass(OpenComputers.getClass, Settings.resourceDomain, "lua/component/data"), "data"))
|
||||
|
||||
@Callback(direct = true, doc = """function():number -- The maximum size of data that can be passed to other functions of the card.""")
|
||||
def getLimit(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(Settings.get.dataCardHardLimit)
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 32, doc = """function(data:string):string -- Applies base64 encoding to the data.""")
|
||||
def encode64(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(Base64.encodeBase64(checkLimits(context, args, Settings.get.dataCardComplex)))
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 32, doc = """function(data:string):string -- Applies base64 decoding to the data.""")
|
||||
def decode64(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(Base64.decodeBase64(checkLimits(context, args, Settings.get.dataCardComplex)))
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 6, doc = """function(data:string):string -- Applies deflate compression to the data.""")
|
||||
def deflate(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = checkLimits(context, args, Settings.get.dataCardComplex)
|
||||
val baos = new ByteArrayOutputStream(512)
|
||||
val deos = new DeflaterOutputStream(baos)
|
||||
deos.write(data)
|
||||
deos.finish()
|
||||
result(baos.toByteArray)
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 6, doc = """function(data:string):string -- Applies inflate decompression to the data.""")
|
||||
def inflate(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = checkLimits(context, args, Settings.get.dataCardComplex)
|
||||
val baos = new ByteArrayOutputStream(512)
|
||||
val inos = new InflaterOutputStream(baos)
|
||||
inos.write(data)
|
||||
inos.finish()
|
||||
result(baos.toByteArray)
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 32, doc = """function(data:string):string -- Computes SHA2-256 hash of the data. Result is in binary format.""")
|
||||
def sha256(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = checkLimits(context, args, Settings.get.dataCardSimple)
|
||||
result(Hashing.sha256().hashBytes(data).asBytes())
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 32, doc = """function(data:string):string -- Computes MD5 hash of the data. Result is in binary format""")
|
||||
def md5(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = checkLimits(context, args, Settings.get.dataCardSimple)
|
||||
result(Hashing.md5().hashBytes(data).asBytes())
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 32, doc = """function(data:string):string -- Computes CRC-32 hash of the data. Result is in binary format""")
|
||||
def crc32(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = checkLimits(context, args, Settings.get.dataCardSimple)
|
||||
result(Hashing.crc32().hashBytes(data).asBytes())
|
||||
}
|
||||
|
||||
private def checkLimits(context: Context, args: Arguments, cost: Double): Array[Byte] = {
|
||||
protected def checkCost(context: Context, args: Arguments, baseCost: Double, byteCost: Double): Array[Byte] = {
|
||||
val data = args.checkByteArray(0)
|
||||
if (data.length > Settings.get.dataCardHardLimit) throw new IllegalArgumentException("data size limit exceeded")
|
||||
if (!node.tryChangeBuffer(-cost)) throw new Exception("not enough energy")
|
||||
if (!node.tryChangeBuffer(-baseCost - data.length * byteCost)) throw new Exception("not enough energy")
|
||||
if (data.length > Settings.get.dataCardSoftLimit) context.pause(Settings.get.dataCardTimeout)
|
||||
data
|
||||
}
|
||||
|
||||
protected def checkCost(baseCost: Double): Unit = {
|
||||
if (!node.tryChangeBuffer(-baseCost)) throw new Exception("not enough energy")
|
||||
}
|
||||
|
||||
protected def trivialCost(context: Context, args: Arguments) =
|
||||
checkCost(context, args, Settings.get.dataCardTrivial, Settings.get.dataCardTrivialByte)
|
||||
|
||||
protected def simpleCost(context: Context, args: Arguments) =
|
||||
checkCost(context, args, Settings.get.dataCardSimple, Settings.get.dataCardSimpleByte)
|
||||
|
||||
protected def complexCost(context: Context, args: Arguments) =
|
||||
checkCost(context, args, Settings.get.dataCardComplex, Settings.get.dataCardComplexByte)
|
||||
|
||||
protected def asymmetricCost(context: Context, args: Arguments) =
|
||||
checkCost(context, args, Settings.get.dataCardAsymmetric, Settings.get.dataCardComplexByte)
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def onConnect(node: Node) {
|
||||
@ -115,3 +76,251 @@ class DataCard extends prefab.ManagedEnvironment {
|
||||
romData.foreach(fs => nbt.setNewCompoundTag("romData", fs.save))
|
||||
}
|
||||
}
|
||||
|
||||
object DataCard {
|
||||
val SecureRandomInstance = new ThreadLocal[SecureRandom]() {
|
||||
override def initialValue = SecureRandom.getInstance("SHA1PRNG")
|
||||
}
|
||||
|
||||
class Tier1 extends DataCard {
|
||||
@Callback(direct = true, doc = """function():number -- The maximum size of data that can be passed to other functions of the card.""")
|
||||
def getLimit(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(Settings.get.dataCardHardLimit)
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 32, doc = """function(data:string):string -- Applies base64 encoding to the data.""")
|
||||
def encode64(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(Base64.encodeBase64(trivialCost(context, args)))
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 32, doc = """function(data:string):string -- Applies base64 decoding to the data.""")
|
||||
def decode64(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(Base64.decodeBase64(trivialCost(context, args)))
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 4, doc = """function(data:string):string -- Applies deflate compression to the data.""")
|
||||
def deflate(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = complexCost(context, args)
|
||||
val baos = new ByteArrayOutputStream(512)
|
||||
val deos = new DeflaterOutputStream(baos)
|
||||
deos.write(data)
|
||||
deos.finish()
|
||||
result(baos.toByteArray)
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 4, doc = """function(data:string):string -- Applies inflate decompression to the data.""")
|
||||
def inflate(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = complexCost(context, args)
|
||||
val baos = new ByteArrayOutputStream(512)
|
||||
val inos = new InflaterOutputStream(baos)
|
||||
inos.write(data)
|
||||
inos.finish()
|
||||
result(baos.toByteArray)
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 4, doc = """function(data:string):string -- Computes SHA2-256 hash of the data. Result is in binary format.""")
|
||||
def sha256(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = complexCost(context, args)
|
||||
result(Hashing.sha256().hashBytes(data).asBytes())
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 8, doc = """function(data:string):string -- Computes MD5 hash of the data. Result is in binary format""")
|
||||
def md5(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = simpleCost(context, args)
|
||||
result(Hashing.md5().hashBytes(data).asBytes())
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 32, doc = """function(data:string):string -- Computes CRC-32 hash of the data. Result is in binary format""")
|
||||
def crc32(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = trivialCost(context, args)
|
||||
result(Hashing.crc32().hashBytes(data).asBytes())
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 32, doc = """function():number -- Returns a tier of the card""")
|
||||
def tier(context: Context, args: Arguments): Array[AnyRef] = result(1)
|
||||
}
|
||||
|
||||
class Tier2 extends Tier1 {
|
||||
@Callback(direct = true, limit = 32, doc = """function():number -- Returns a tier of the card""")
|
||||
override def tier(context: Context, args: Arguments): Array[AnyRef] = result(2)
|
||||
|
||||
private def crypt(context: Context, args: Arguments, mode: Int): Array[AnyRef] = {
|
||||
val data = simpleCost(context, args)
|
||||
|
||||
val key = args.checkByteArray(1)
|
||||
if (key.length != 16)
|
||||
throw new IllegalArgumentException("Expected a 128-bit AES key")
|
||||
|
||||
val iv = args.checkByteArray(2)
|
||||
if (iv.length != 16)
|
||||
throw new IllegalArgumentException("Expected a 128-bit AES IV")
|
||||
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
cipher.init(mode, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv))
|
||||
result(cipher.doFinal(data))
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 8, doc = """function(data:string, key: string, iv:string):string -- Encrypt data with AES. Result is in binary format.""")
|
||||
def encrypt(context: Context, args: Arguments): Array[AnyRef] = crypt(context, args, Cipher.ENCRYPT_MODE)
|
||||
|
||||
@Callback(direct = true, limit = 8, doc = """function(data:string, key:string, iv:string):string -- Decrypt data with AES""")
|
||||
def decrypt(context: Context, args: Arguments): Array[AnyRef] = crypt(context, args, Cipher.DECRYPT_MODE)
|
||||
|
||||
private def hash(context: Context, args: Arguments, mode: String, hmacMode: String, simple: Boolean = false): Array[AnyRef] = {
|
||||
val data = if (simple) simpleCost(context, args) else complexCost(context, args)
|
||||
|
||||
if (args.count() > 1) {
|
||||
val key = args.checkByteArray(1)
|
||||
|
||||
val hmac = Mac.getInstance(hmacMode)
|
||||
hmac.init(new SecretKeySpec(key, hmacMode))
|
||||
result(hmac.doFinal(data))
|
||||
} else {
|
||||
result(MessageDigest.getInstance(mode).digest(data))
|
||||
}
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 4, doc = """function(data:string[, hmacKey:string]):string -- Computes SHA2-256 hash of the data. Result is in binary format.""")
|
||||
override def sha256(context: Context, args: Arguments): Array[AnyRef] = hash(context, args, "SHA-256", "HmacSHA256")
|
||||
|
||||
@Callback(direct = true, limit = 8, doc = """function(data:string[, hmacKey:string]):string -- Computes MD5 hash of the data. Result is in binary format""")
|
||||
override def md5(context: Context, args: Arguments): Array[AnyRef] = hash(context, args, "MD5", "HmacMD5", simple = true)
|
||||
|
||||
@Callback(direct = true, limit = 4, doc = """function(len:number):string -- Generates secure random binary data""")
|
||||
def random(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
checkCost(Settings.get.dataCardComplex)
|
||||
val len = args.checkInteger(0)
|
||||
|
||||
if (len <= 0 || len > 1024)
|
||||
throw new IllegalArgumentException("Length must be in range [1..1024]")
|
||||
|
||||
val target = new Array[Byte](len)
|
||||
SecureRandomInstance.get.nextBytes(target)
|
||||
result(target)
|
||||
}
|
||||
}
|
||||
|
||||
object ECUserdata {
|
||||
def deserializeKey(t: String, data: Array[Byte]): Key = {
|
||||
val fact = KeyFactory.getInstance("EC")
|
||||
|
||||
t match {
|
||||
case "ec-private" => fact.generatePrivate(new PKCS8EncodedKeySpec(data))
|
||||
case "ec-public" => fact.generatePublic(new X509EncodedKeySpec(data))
|
||||
case _ => throw new IllegalArgumentException("Wrong key type. Currently supported: ec-public, ec-private")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ECUserdata extends prefab.AbstractValue {
|
||||
var k: Key = null
|
||||
|
||||
// Hack to keep empty constructor for deserialization
|
||||
def this(_k: Key) = {
|
||||
this()
|
||||
k = _k
|
||||
}
|
||||
|
||||
private def keyType = k match {
|
||||
case x: ECPrivateKey => "ec-private"
|
||||
case x: ECPublicKey => "ec-public"
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 32, doc = "function():string -- Returns type of key")
|
||||
def keyType(context: Context, args: Arguments): Array[AnyRef] = result(keyType)
|
||||
|
||||
@Callback(direct = true, limit = 4, doc = "function():string -- Returns string representation of key. Result is in binary format.")
|
||||
def serialize(context: Context, args: Arguments): Array[AnyRef] = result(k.getEncoded)
|
||||
|
||||
@Callback(direct = true, limit = 32, doc = "function():boolean -- Returns whether key is public")
|
||||
def isPublic(context: Context, args: Arguments): Array[AnyRef] = result(isPublic)
|
||||
|
||||
def isPublic = k.isInstanceOf[ECPublicKey]
|
||||
|
||||
override def load(nbt: NBTTagCompound): Unit =
|
||||
k = ECUserdata.deserializeKey(nbt.getString("Type"), nbt.getByteArray("Data"))
|
||||
|
||||
override def save(nbt: NBTTagCompound): Unit = {
|
||||
nbt.setString("Type", keyType)
|
||||
nbt.setByteArray("Data", k.getEncoded)
|
||||
}
|
||||
}
|
||||
|
||||
class Tier3 extends Tier2 {
|
||||
@Callback(direct = true, limit = 32, doc = """function():number -- Returns a tier of the card""")
|
||||
override def tier(context: Context, args: Arguments): Array[AnyRef] = result(3)
|
||||
|
||||
@Callback(direct = true, limit = 1, doc = """function([bitLen:number]):eckey,eckey -- Generates key pair. Returns: public, private keys. Allowed key lengths: 256, 384 bits""")
|
||||
def generateKeyPair(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
checkCost(Settings.get.dataCardAsymmetric)
|
||||
var bitLen = 384
|
||||
|
||||
if (args.count() > 0) {
|
||||
bitLen = args.checkInteger(0)
|
||||
|
||||
if (bitLen != 256 && bitLen != 384)
|
||||
throw new IllegalArgumentException("Invalid key length. Allowed: 256, 384")
|
||||
}
|
||||
|
||||
val kpg = KeyPairGenerator.getInstance("EC")
|
||||
kpg.initialize(bitLen, SecureRandomInstance.get)
|
||||
val kp = kpg.generateKeyPair()
|
||||
|
||||
result(new ECUserdata(kp.getPublic), new ECUserdata(kp.getPrivate))
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 8, doc = """function(data:string, type:string):eckey -- Restores key from its string representation.""")
|
||||
def deserializeKey(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = simpleCost(context, args)
|
||||
val t = args.checkString(1)
|
||||
|
||||
result(new ECUserdata(ECUserdata.deserializeKey(t, data)))
|
||||
}
|
||||
|
||||
private def checkUserdata(args: Arguments, i: Int, isPublic: Boolean = false, anyAccepted: Boolean = false) =
|
||||
args.checkAny(i) match {
|
||||
case x: ECUserdata =>
|
||||
if (anyAccepted || x.isPublic == isPublic) x
|
||||
else throw new IllegalArgumentException((if (isPublic) "Public" else "Private") + " key expected at " + i)
|
||||
case x => throw new IllegalArgumentException("Userdata expected at " + i)
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 1, doc = """function(priv:eckey, pub:eckey):string -- Generates a shared key. ecdh(a.priv, b.pub) == ecdh(b.priv, a.pub)""")
|
||||
def ecdh(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
checkCost(Settings.get.dataCardAsymmetric)
|
||||
val privKey = checkUserdata(args, 0, isPublic = false).k
|
||||
val pubKey = checkUserdata(args, 1, isPublic = true).k
|
||||
|
||||
val ka = KeyAgreement.getInstance("ECDH")
|
||||
ka.init(privKey)
|
||||
ka.doPhase(pubKey, true)
|
||||
result(ka.generateSecret)
|
||||
}
|
||||
|
||||
@Callback(direct = true, limit = 1, doc = """function(data:string, key:eckey[, sig:string]):string|boolean -- Signs or verifies data""")
|
||||
def ecdsa(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val data = asymmetricCost(context, args)
|
||||
val key = checkUserdata(args, 1, anyAccepted = true)
|
||||
val sig = args.optByteArray(2, null)
|
||||
|
||||
val sign = Signature.getInstance("SHA256withECDSA")
|
||||
if (sig != null) {
|
||||
// Verify mode
|
||||
if (!key.isPublic)
|
||||
throw new IllegalArgumentException("Public key expected")
|
||||
|
||||
sign.initVerify(key.k.asInstanceOf[PublicKey])
|
||||
sign.update(data)
|
||||
result(sign.verify(sig))
|
||||
} else {
|
||||
// Sign mode
|
||||
if (key.isPublic)
|
||||
throw new IllegalArgumentException("Private key expected")
|
||||
|
||||
sign.initSign(key.k.asInstanceOf[PrivateKey])
|
||||
sign.update(data)
|
||||
result(sign.sign())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user