|
| 1 | +using Base: SecretBuffer, SecretBuffer!, shred! |
| 2 | +import ..Common: decrypt |
| 3 | + |
| 4 | +function store_key(s::SecHandler, cfn::CosName, |
| 5 | + data::Tuple{UInt32, SecretBuffer}) |
| 6 | + skey = SecretBuffer!(read(s.skey_path, 32)) |
| 7 | + iv = SecretBuffer!(read(s.iv_path, 16)) |
| 8 | + cctx = CipherContext("aes_256_cbc", skey, iv, true) |
| 9 | + shred!(skey); shred!(iv) |
| 10 | + |
| 11 | + perm, key = data |
| 12 | + c = update!(cctx, key) |
| 13 | + append!(c, close(cctx)) |
| 14 | + s.keys[cfn] = (perm, c) |
| 15 | + return data |
| 16 | +end |
| 17 | + |
| 18 | +function get_key(s::SecHandler, cfn::CosName) |
| 19 | + permkey = get(s.keys, cfn, nothing) |
| 20 | + permkey === nothing && return nothing |
| 21 | + |
| 22 | + skey = SecretBuffer!(read(s.skey_path, 32)) |
| 23 | + iv = SecretBuffer!(read(s.iv_path, 16)) |
| 24 | + cctx = CipherContext("aes_256_cbc", skey, iv, false) |
| 25 | + shred!(skey); shred!(iv) |
| 26 | + |
| 27 | + perm, c = permkey |
| 28 | + b = update!(cctx, c) |
| 29 | + append!(b, close(cctx)) |
| 30 | + return perm, SecretBuffer!(b) |
| 31 | +end |
| 32 | + |
| 33 | +function get_cfm(h::SecHandler, cfn::CosName) |
| 34 | + cfn === cn"Identity" && return cn"None" |
| 35 | + cfm = get(get(h.cf, cfn), cn"CFM") |
| 36 | + cfm in [cn"None", cn"V2", cn"AESV2", cn"AESV3"] || error(E_INVALID_CRYPT) |
| 37 | + return cfm |
| 38 | +end |
| 39 | + |
1 | 40 | struct CryptParams
|
2 | 41 | num::Int
|
3 | 42 | gen::Int
|
4 | 43 | cfn::CosName
|
5 | 44 | end
|
6 | 45 |
|
| 46 | +CryptParams(h::SecHandler, oi::CosIndirectObject) = |
| 47 | + CryptParams(h, oi.num, oi.gen, oi.obj) |
| 48 | + |
| 49 | +CryptParams(h::SecHandler, num::Int, gen::Int, o::CosObject) = |
| 50 | + h.r < 4 ? CryptParams(num, gen, cn"StdCF") : error(E_INVALID_OBJECT) |
| 51 | + |
| 52 | +CryptParams(h::SecHandler, num::Int, gen::Int, o::CosString) = |
| 53 | + CryptParams(num, gen, h.strf) |
| 54 | + |
| 55 | +CryptParams(h::SecHandler, num::Int, gen::Int, o::CosObjectStream) = |
| 56 | + CryptParams(h, num, gen, o.stm) |
| 57 | + |
| 58 | +# For Crypt filter chain ensure the filters before the crypt filters are removed. |
| 59 | +# Crypt filter parameters are associated with the document and the CosStream as |
| 60 | +# such may not have access to such parameters. Hence, the crypt filter has to be |
| 61 | +# decrypted when the document information is available. |
| 62 | +function CryptParams(h::SecHandler, num::Int, gen::Int, o::CosStream) |
| 63 | + cfn = h.stmf |
| 64 | + filters = get(o, cn"FFilter") |
| 65 | + filters === CosNull && return CryptParams(num, gen, cfn) |
| 66 | + if cn"Crypt" === filters || |
| 67 | + (filters isa CosArray && length(filters) > 0 && cn"Crypt" === filters[1]) |
| 68 | + params = get(o, cn"FDecodeParms", CosDict()) |
| 69 | + param = params isa CosDict ? params : params[1] |
| 70 | + cfn = get(param, cn"Name", cn"Identity") |
| 71 | + end |
| 72 | + return CryptParams(num, gen, cfn) |
| 73 | +end |
| 74 | + |
| 75 | +function get_key(h::SecHandler, params::CryptParams) |
| 76 | + vpw = get_key(h, params.cfn) |
| 77 | + vpw === nothing || return vpw |
| 78 | + return get_key(h, params.cfn, h.access) |
| 79 | +end |
| 80 | + |
| 81 | +function algo01(h::SecHandler, params::CryptParams, |
| 82 | + data::AbstractVector{UInt8}, isencrypt::Bool) |
| 83 | + num, gen, cfn = params.num, params.gen, params.cfn |
| 84 | + cfm = get_cfm(h, cfn) |
| 85 | + isRC4 = cfm === cn"V2" |
| 86 | + numarr = copy(reinterpret(UInt8, [num])) |
| 87 | + ENDIAN_BOM == 0x01020304 && reverse!(numarr) |
| 88 | + numarr = numarr[1:3] |
| 89 | + genarr = copy(reinterpret(UInt8, [gen])) |
| 90 | + ENDIAN_BOM == 0x01020304 && reverse!(genarr) |
| 91 | + genarr = genarr[1:2] |
| 92 | + perm, key = get_key(h, params) |
| 93 | + n = div(h.length, 8) |
| 94 | + md = shred!(key) do kc |
| 95 | + n != kc.size && error("Invalid encryption key length") |
| 96 | + seekend(kc); write(kc, numarr); write(kc, genarr) |
| 97 | + !isRC4 && write(kc, AES_SUFFIX) |
| 98 | + mdctx = DigestContext("md5") |
| 99 | + update!(mdctx, kc) |
| 100 | + return close(mdctx) |
| 101 | + end |
| 102 | + l = min(n+5, 16) |
| 103 | + key = SecretBuffer!(md[1:l]) |
| 104 | + iv = SecretBuffer!(isRC4 ? UInt8[] : |
| 105 | + isencrypt ? crypto_random(16) : data[1:16]) |
| 106 | + try |
| 107 | + cctx = CipherContext(isRC4 ? "rc4" : "aes_128_cbc", key, iv, isencrypt) |
| 108 | + d = (isRC4 || isencrypt) ? update!(cctx, data) : |
| 109 | + update!(cctx, (@view data[17:end])) |
| 110 | + append!(d, close(cctx)) |
| 111 | + return d |
| 112 | + finally |
| 113 | + shred!(key); shred!(iv) |
| 114 | + end |
| 115 | + end |
| 116 | + |
| 117 | +function algo01a(h::SecHandler, params::CryptParams, |
| 118 | + data::AbstractVector{UInt8}, isencrypt::Bool) |
| 119 | + perm, key = get_key(h, params) |
| 120 | + iv = SecretBuffer!(isencrypt ? crypto_random(16) : data[1:16]) |
| 121 | + try |
| 122 | + cctx = CipherContext("aes_256_cbc", key, iv, isencrypt) |
| 123 | + d = isencrypt ? update!(cctx, data) : update!(cctx, (@view data[17:end])) |
| 124 | + append!(d, close(cctx)) |
| 125 | + return d |
| 126 | + finally |
| 127 | + shred!(key); shred!(iv) |
| 128 | + end |
| 129 | +end |
| 130 | + |
| 131 | +function crypt(h::SecHandler, params::CryptParams, |
| 132 | + data::Vector{UInt8}, isencrypt::Bool) |
| 133 | + cfm = get_cfm(h, params.cfn) |
| 134 | + cfm === cn"None" && return data |
| 135 | + cfm === cn"AESV3" && return algo01a(h, params, data, isencrypt) |
| 136 | + return algo01(h, params, data, isencrypt) |
| 137 | +end |
| 138 | + |
7 | 139 | decrypt!(::Union{Nothing, SecHandler}, obj) = obj
|
8 | 140 |
|
9 | 141 | # Even when document has no security handler the object stream needs to be
|
@@ -50,29 +182,18 @@ function decrypt(h::SecHandler, params::CryptParams, o::CosStream)
|
50 | 182 | set!(o, cn"Length", CosInt(len))
|
51 | 183 |
|
52 | 184 | filters = get(o, cn"FFilter")
|
53 |
| - if filters !== CosNull |
54 |
| - if filters isa CosName |
55 |
| - if cn"Crypt" === filters |
56 |
| - set!(o, cn"FFilter", CosNull) |
57 |
| - set!(o, cn"FDecodeParms", CosNull) |
58 |
| - end |
59 |
| - else |
60 |
| - vf = get(filters) |
61 |
| - l = length(vf) |
62 |
| - if vf[1] === cn"Crypt" |
63 |
| - if l == 1 |
64 |
| - set!(o, cn"FFilter", CosNull) |
65 |
| - set!(o, cn"FDecodeParms", CosNull) |
66 |
| - else |
67 |
| - filters = get(o, cn"FFilter") |
68 |
| - deleteat!(get(filters), 1) |
69 |
| - params = get(o, cn"FDecodeParms") |
70 |
| - params !== CosNull && deleteat!(get(params), 1) |
71 |
| - end |
72 |
| - end |
73 |
| - end |
| 185 | + if filters isa CosNullType || |
| 186 | + (filters isa CosName && cn"Crypt" === filters) || |
| 187 | + (filters isa CosArray && |
| 188 | + (length(filters) == 0 || |
| 189 | + (length(filters) == 1 && cn"Crypt" === filters[1]))) |
| 190 | + set!(o, cn"FFilter", CosNull) |
| 191 | + set!(o, cn"FDecodeParms", CosNull) |
| 192 | + elseif filters isa CosArray && cn"Crypt" === filters[1] |
| 193 | + deleteat!(get(filters), 1) |
| 194 | + params = get(o, cn"FDecodeParms") |
| 195 | + params !== CosNull && deleteat!(get(params), 1) |
74 | 196 | end
|
75 |
| - |
76 | 197 | o.isInternal = false
|
77 | 198 | return o
|
78 | 199 | end
|
|
0 commit comments