diff --git a/README.md b/README.md index e69de29..9370599 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,42 @@ +OpenSSL +========== + +fork of [OpenSSL](https://github.com/dirk/OpenSSL.jl) + +convert for julia 0.4- + + +# how to use it + +```julia +import OpenSSL + +OpenSSL.init() +s = OpenSSL.Digest.digest("SHA512", "test") +m = OpenSSL.Digest.digest("MD5", "test") +OpenSSL.cleanup() +``` + + +# see also + +[AES256CBC](https://github.com/HatsuneMiku/AES256CBC.jl) + +```julia +# AES256CBC encrypt/decrypt +using AES256CBC +# typealias UBytes Array{UInt8, 1} +plain = string2bytes("Message") # UBytes +passwd = string2bytes("Secret Passphrase") # UBytes +salt = genRandUBytes(8) # UBytes +(key32, iv16) = genKey32Iv16(passwd, salt) # (UBytes, UBytes) +encoded = encryptAES256CBC(key32, iv16, plain) # UBytes +decoded = decryptAES256CBC(key32, iv16, encoded) # UBytes +``` + + +# status + +[![Build Status _dev_aes256cbc](https://travis-ci.org/HatsuneMiku/OpenSSL.jl.svg?branch=_dev_aes256cbc)](https://travis-ci.org/HatsuneMiku/OpenSSL.jl) + +[![Build Status master](https://travis-ci.org/HatsuneMiku/OpenSSL.jl.svg?branch=master)](https://travis-ci.org/HatsuneMiku/OpenSSL.jl) diff --git a/REQUIRE b/REQUIRE index e69de29..69345da 100644 --- a/REQUIRE +++ b/REQUIRE @@ -0,0 +1 @@ +julia 0.4- diff --git a/src/OpenSSL.jl b/src/OpenSSL.jl index 0a3a1cd..c023a41 100644 --- a/src/OpenSSL.jl +++ b/src/OpenSSL.jl @@ -1,54 +1,61 @@ +# OpenSSL + +VERSION >= v"0.4.0-dev+6521" && __precompile__() module OpenSSL - import Base - - const LIBCRYPTO = "libcrypto" - + + const LIBCRYPTO = @windows ? "libeay32" : "libcrypto" + + function init() + # ccall((:OpenSSL_add_all_digests, OpenSSL.LIBCRYPTO), Void, ()) + # ccall((:OpenSSL_add_all_ciphers, OpenSSL.LIBCRYPTO), Void, ()) + # ccall((:OPENSSL_add_all_algorithms_conf, OpenSSL.LIBCRYPTO), Void, ()) + ccall((:OPENSSL_add_all_algorithms_noconf, OpenSSL.LIBCRYPTO), Void, ()) + # alias OpenSSL_add_all_algorithms :OPENSSL_add_all_algorithms_noconf + end + + function cleanup() + ccall((:EVP_cleanup, OpenSSL.LIBCRYPTO), Void, ()) + end + module Digest import OpenSSL - import Base.ccall - + function init() ccall((:OpenSSL_add_all_digests, OpenSSL.LIBCRYPTO), Void, ()) end + function cleanup() ccall((:EVP_cleanup, OpenSSL.LIBCRYPTO), Void, ()) end - - function hexstring(hexes::Array{Uint8,1}) - join([hex(h,2) for h in hexes], "") - end - - function digest(name::String, data::String) + + function digest(name::AbstractString, bs::Array{UInt8,1}) ctx = ccall((:EVP_MD_CTX_create, OpenSSL.LIBCRYPTO), Ptr{Void}, ()) try # Get the message digest struct - md = ccall((:EVP_get_digestbyname, OpenSSL.LIBCRYPTO), Ptr{Void}, (Ptr{Uint8},), bytestring(name)) + md = ccall((:EVP_get_digestbyname, OpenSSL.LIBCRYPTO), Ptr{Void}, (Ptr{UInt8},), name) if(md == C_NULL) error("Unknown message digest $name") end # Add the digest struct to the context ccall((:EVP_DigestInit_ex, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{Void}, Ptr{Void}), ctx, md, C_NULL) - # Update the context with the input data - bs = bytestring(data) - ccall((:EVP_DigestUpdate, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{Uint8}, Uint), ctx, bs, length(bs)) + # Update the context with the input data : bs + ccall((:EVP_DigestUpdate, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{UInt8}, UInt), ctx, bs, sizeof(bs)) # Figure out the size of the output string for the digest - size = ccall((:EVP_MD_size, OpenSSL.LIBCRYPTO), Uint, (Ptr{Void},), md) - uval = Array(Uint8, size) + size = ccall((:EVP_MD_size, OpenSSL.LIBCRYPTO), UInt, (Ptr{Void},), md) + uval = Array(UInt8, size) # Calculate the digest and store it in the uval array - ccall((:EVP_DigestFinal_ex, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{Uint8}, Ptr{Uint}), ctx, uval, C_NULL) - # bytestring(uval) - # Convert the uval array to a string of hexes - return hexstring(uval) + ccall((:EVP_DigestFinal_ex, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{UInt8}, Ptr{UInt}), ctx, uval, C_NULL) + return uval finally ccall((:EVP_MD_CTX_destroy, OpenSSL.LIBCRYPTO), Void, (Ptr{Void},), ctx) end end#/digest - function digestinit(name::String) + function digestinit(name::AbstractString) ctx = ccall((:EVP_MD_CTX_create, OpenSSL.LIBCRYPTO), Ptr{Void}, ()) try # Get the message digest struct - md = ccall((:EVP_get_digestbyname, OpenSSL.LIBCRYPTO), Ptr{Void}, (Ptr{Uint8},), bytestring(name)) + md = ccall((:EVP_get_digestbyname, OpenSSL.LIBCRYPTO), Ptr{Void}, (Ptr{UInt8},), name) if(md == C_NULL) error("Unknown message digest $name") end @@ -60,39 +67,147 @@ module OpenSSL ccall((:EVP_MD_CTX_destroy, OpenSSL.LIBCRYPTO), Void, (Ptr{Void},), ctx) nothing end - end#/digest + end#/digestinit - function digestupdate(ctx,data::String) + function digestupdate(ctx, bs::Array{UInt8,1}) try - # Update the context with the input data - bs = bytestring(data) - ccall((:EVP_DigestUpdate, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{Uint8}, Uint), ctx, bs, length(bs)) + # Update the context with the input data : bs + ccall((:EVP_DigestUpdate, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{UInt8}, UInt), ctx, bs, sizeof(bs)) ctx catch ccall((:EVP_MD_CTX_destroy, OpenSSL.LIBCRYPTO), Void, (Ptr{Void},), ctx) nothing end - end#/digest - + end#/digestupdate + function digestfinalize(ctx) try # Get the message digest struct - md = ccall((:EVP_MD_CTX_md, OpenSSL.LIBCRYPTO), Ptr{Void}, (Ptr{Uint8},), ctx) + md = ccall((:EVP_MD_CTX_md, OpenSSL.LIBCRYPTO), Ptr{Void}, (Ptr{UInt8},), ctx) if(md == C_NULL) error("Unknown message digest $name") end - size = ccall((:EVP_MD_size, OpenSSL.LIBCRYPTO), Uint, (Ptr{Void},), md) - uval = Array(Uint8, size) + size = ccall((:EVP_MD_size, OpenSSL.LIBCRYPTO), UInt, (Ptr{Void},), md) + uval = Array(UInt8, size) # Calculate the digest and store it in the uval array - ccall((:EVP_DigestFinal_ex, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{Uint8}, Ptr{Uint}), ctx, uval, C_NULL) - # bytestring(uval) - # Convert the uval array to a string of hexes - return hexstring(uval) + ccall((:EVP_DigestFinal_ex, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{UInt8}, Ptr{UInt}), ctx, uval, C_NULL) + return uval finally ccall((:EVP_MD_CTX_destroy, OpenSSL.LIBCRYPTO), Void, (Ptr{Void},), ctx) end - end#/digest + end#/digestfinalize end#/Digest - + + module Cipher + import OpenSSL + + function init() + ccall((:OpenSSL_add_all_ciphers, OpenSSL.LIBCRYPTO), Void, ()) + end + + function cleanup() + ccall((:EVP_cleanup, OpenSSL.LIBCRYPTO), Void, ()) + end + + function get_EVP_CIPHER(name::AbstractString) + # ec = ccall((:EVP_get_cipherbyname, OpenSSL.LIBCRYPTO), Ptr{Void}, (Ptr{UInt8},), name) + algorithm = "ccall((:EVP_$(name), OpenSSL.LIBCRYPTO), Ptr{Void}, ())" + ec = eval(parse(algorithm)) + # ec = ccall((:EVP_aes_256_cbc, OpenSSL.LIBCRYPTO), Ptr{Void}, ()) + if(ec == C_NULL) + error("Unknown cipher algorithm $name") + end + return ec + end + + function encrypt(name::AbstractString, key::Array{UInt8,1}, iv::Array{UInt8,1}, plain::Array{UInt8,1}, selfpad::Bool=false) + ctx = ccall((:EVP_CIPHER_CTX_new, OpenSSL.LIBCRYPTO), Ptr{Void}, ()) + ccall((:EVP_CIPHER_CTX_init, OpenSSL.LIBCRYPTO), Void, (Ptr{Void},), ctx) + try + ec = get_EVP_CIPHER(name) + ccall((:EVP_EncryptInit_ex, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{Void}, Ptr{Void}, Ptr{UInt8}, Ptr{UInt8}), ctx, ec, C_NULL, key, iv) + + # :EVP_CIPHER_CTX_block_size must be after :EVP_EncryptInit_ex + blksize = ccall((:EVP_CIPHER_CTX_block_size, OpenSSL.LIBCRYPTO), UInt, (Ptr{Void},), ctx) + if(selfpad) + ccall((:EVP_CIPHER_CTX_set_padding, OpenSSL.LIBCRYPTO), UInt, (Ptr{Void}, UInt), ctx, 0) # disable + end + remain = sizeof(plain) % blksize + padlen = blksize - remain + enclen = sizeof(plain) + padlen + enc = Array(UInt8, enclen) + outlen = UInt(1) # start position = 1 + tmpenc = Array(UInt8, blksize) + tmplen = Ref{Cint}(0) + + for i in 1:div(sizeof(plain), blksize) + ccall((:EVP_EncryptUpdate, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{UInt8}, Ref{Cint}, Ptr{UInt8}, UInt), ctx, tmpenc, tmplen, plain[outlen:outlen+blksize-1], blksize) + if(tmplen[] > 0) enc[outlen:outlen+blksize-1] = tmpenc[1:blksize] end + outlen += tmplen[] + end + + if(remain > 0) + ccall((:EVP_EncryptUpdate, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{UInt8}, Ref{Cint}, Ptr{UInt8}, UInt), ctx, tmpenc, tmplen, plain[outlen:outlen+remain-1], remain) + if(tmplen[] > 0) enc[outlen:outlen+remain-1] = tmpenc[1:remain] end + outlen += tmplen[] + end + + if(selfpad && padlen != 0) # no use (padlen > 0), UInt is everytime > 0 + # skip + # outlen += tmplen[] + end + + if(!selfpad) + ccall((:EVP_EncryptFinal_ex, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{UInt8}, Ref{Cint}), ctx, tmpenc, tmplen) + if(tmplen[] > 0) enc[outlen:outlen+blksize-1] = tmpenc[1:blksize] end + outlen += tmplen[] + end + + ccall((:EVP_CIPHER_CTX_cleanup, OpenSSL.LIBCRYPTO), Void, (Ptr{Void},), ctx) + return enc + finally + ccall((:EVP_CIPHER_CTX_free, OpenSSL.LIBCRYPTO), Void, (Ptr{Void},), ctx) + end + end#/encrypt + + function decrypt(name::AbstractString, key::Array{UInt8,1}, iv::Array{UInt8,1}, cipher::Array{UInt8,1}, selfpad::Bool=false) + ctx = ccall((:EVP_CIPHER_CTX_new, OpenSSL.LIBCRYPTO), Ptr{Void}, ()) + ccall((:EVP_CIPHER_CTX_init, OpenSSL.LIBCRYPTO), Void, (Ptr{Void},), ctx) + try + ec = get_EVP_CIPHER(name) + ccall((:EVP_DecryptInit_ex, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{Void}, Ptr{Void}, Ptr{UInt8}, Ptr{UInt8}), ctx, ec, C_NULL, key, iv) + + # :EVP_CIPHER_CTX_block_size must be after :EVP_DecryptInit_ex + blksize = ccall((:EVP_CIPHER_CTX_block_size, OpenSSL.LIBCRYPTO), UInt, (Ptr{Void},), ctx) + if(selfpad) + ccall((:EVP_CIPHER_CTX_set_padding, OpenSSL.LIBCRYPTO), UInt, (Ptr{Void}, UInt), ctx, 0) # disable + end + declen = sizeof(cipher) # trim padlen later + dec = Array(UInt8, declen) + outlen = UInt(1) # start position = 1 + tmpdec = Array(UInt8, blksize) + tmplen = Ref{Cint}(0) + + for i in 1:div(sizeof(cipher), blksize) + ccall((:EVP_DecryptUpdate, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{UInt8}, Ref{Cint}, Ptr{UInt8}, UInt), ctx, tmpdec, tmplen, cipher[outlen:outlen+blksize-1], blksize) + if(tmplen[] > 0) dec[outlen:outlen+blksize-1] = tmpdec[1:blksize] end + outlen += tmplen[] + end + + if(!selfpad) + ccall((:EVP_DecryptFinal_ex, OpenSSL.LIBCRYPTO), Void, (Ptr{Void}, Ptr{UInt8}, Ref{Cint}), ctx, tmpdec, tmplen) + if(tmplen[] > 0) dec[outlen:outlen+blksize-1] = tmpdec[1:blksize] end + outlen += tmplen[] + end + + ccall((:EVP_CIPHER_CTX_cleanup, OpenSSL.LIBCRYPTO), Void, (Ptr{Void},), ctx) + return selfpad ? dec[1:declen-dec[declen]] : dec[1:outlen-1] + finally + ccall((:EVP_CIPHER_CTX_free, OpenSSL.LIBCRYPTO), Void, (Ptr{Void},), ctx) + end + end#/decrypt + + end#/Cipher + end#/OpenSSL diff --git a/test/data.jl b/test/data.jl index f5b1036..b2f2390 100644 --- a/test/data.jl +++ b/test/data.jl @@ -1,2 +1,5 @@ -sha512_of_test = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff" -md5_of_test = "098f6bcd4621d373cade4e832627b4f6" +sha512_of_test = hex2bytes("ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff") +md5_of_test = hex2bytes("098f6bcd4621d373cade4e832627b4f6") +md5_of_bytes = hex2bytes("e299ff9d8e4831f07e5323913c53e5f0") +aes256cbc_of_shortdata = hex2bytes("da8aab1b904205a7e49c1ecc7118a8f4") +aes256cbc_of_longdata = hex2bytes("da8aab1b904205a7e49c1ecc7118a8f4804bef7be79216196739de7845da182d") diff --git a/test/runtests.jl b/test/runtests.jl new file mode 100644 index 0000000..06f888c --- /dev/null +++ b/test/runtests.jl @@ -0,0 +1,4 @@ +using OpenSSL +using Base.Test + +include("test.jl") diff --git a/test/test.jl b/test/test.jl index 89f4342..d385421 100644 --- a/test/test.jl +++ b/test/test.jl @@ -1,12 +1,52 @@ -require("OpenSSL") +import OpenSSL +using Base.Test include("data.jl") -assert(isdefined(:OpenSSL)) -OpenSSL.Digest.init() +@test isdefined(:OpenSSL) == true +OpenSSL.init() -assert(OpenSSL.Digest.digest("SHA512", "test") == sha512_of_test) -assert(OpenSSL.Digest.digest("MD5", "test") == md5_of_test) +s = OpenSSL.Digest.digest("SHA512", "test".data) +println(s) +@test s == sha512_of_test -OpenSSL.Digest.cleanup() +m = OpenSSL.Digest.digest("MD5", "test".data) +println(m) +@test m == md5_of_test + +h = OpenSSL.Digest.digest("MD5", + hex2bytes("5365637265742050617373706872617365a3e550e89e70996c")) +println(h) +@test h == md5_of_bytes + +key32 = hex2bytes("e299ff9d8e4831f07e5323913c53e5f0"* + "fec3a040a211d6562fa47607244d0051") +iv16 = hex2bytes("7c7ed9434ddb9c2d1e1fcc38b4bf4667") + +### selfpad=true +#plainshort = "Message\t\t\t\t\t\t\t\t\t".data +#plainlong = "Message\t\t\t\t\t\t\t\t\t"* +# "\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10".data + +### selfpad=false +plainshort = "Message".data +plainlong = "Message\t\t\t\t\t\t\t\t\t".data + +es = OpenSSL.Cipher.encrypt("aes_256_cbc", key32, iv16, plainshort) +println(es) +@test es == aes256cbc_of_shortdata + +ds = OpenSSL.Cipher.decrypt("aes_256_cbc", key32, iv16, es) +println(ds) +@test ds == plainshort + +el = OpenSSL.Cipher.encrypt("aes_256_cbc", key32, iv16, plainlong) +println(el) +@test el == aes256cbc_of_longdata + +dl = OpenSSL.Cipher.decrypt("aes_256_cbc", key32, iv16, el) +println(dl) +@test dl == plainlong + +OpenSSL.cleanup() println("All tests passed")