Current section

Files

Jump to
ca lib CAdES.ex
Raw

lib/CAdES.ex

defmodule CA.CAdES do
@moduledoc "CAdES (804) Qualified Digital Signature library."
require Record
Record.defrecord(:certinfo, serial: "", cn: "", givenName: "", surname: "", timeStamp: "",
attrs: [], o: "", title: "", ou: "", c: "", locality: "")
def subj({:rdnSequence, attrs}) do
{:rdnSequence, :lists.map(fn
[{t,oid,{:uTF8String,x}}] ->
[{t,oid,:asn1rt_nif.encode_ber_tlv({12, :erlang.iolist_to_binary(x)})}]
[{t,oid,x}] when is_list(x) ->
[{t,oid,:asn1rt_nif.encode_ber_tlv({19, :erlang.iolist_to_binary(x)})}]
[{t,oid,x}] -> [{t,oid,x}] end, attrs)}
end
def unsubj({:rdnSequence, attrs}) do
{:rdnSequence, :lists.map(fn [{t,oid,x}] ->
case :asn1rt_nif.decode_ber_tlv(x) do
{{12,a},_} -> [{t,oid,{:uTF8String,a}}]
{{19,a},_} -> [{t,oid,:erlang.binary_to_list(a)}]
end end, attrs)}
end
def readSignature(name \\ "2.p7s") do
{:ok, bin} = :file.read_file name
ber = parseSignData(bin)
ber
end
def extract(code, person) do
case :lists.keyfind(code, 2, person) do
false -> ""
{_, _, {:printable, str}} -> str
{_, _, {:utf8, str}} -> str
end
end
def parseSignData(bin) do
{_, {:ContentInfo, oid, ci}} = :KEP.decode(:ContentInfo, bin)
{:ok, {:SignedData, _, alg, x, c, x1, si}} = :KEP.decode(:SignedData, ci)
parseSignDataCert({alg,oid,x,c,x1,si})
end
def parseSignDataCert({_,_,_,:asn1_NOVALUE,_,_}), do: []
def parseSignDataCert({_,_,_,[cert],_,attr}), do: parseCert(cert,attr)
def parseAttrs(attrs, options) do
certinfo(
o: extract({2, 5, 4, 10}, attrs),
ou: extract({2, 5, 4, 11}, attrs),
title: extract({2, 5, 4, 12}, attrs),
cn: extract({2, 5, 4, 3}, attrs),
timeStamp: extract({2, 5, 4, 3}, attrs),
givenName: extract({2, 5, 4, 42}, attrs),
surname: extract({2, 5, 4, 4}, attrs),
locality: extract({2, 5, 4, 7}, attrs),
serial: extract({2, 5, 4, 5}, attrs),
c: extract({2, 5, 4, 6}, attrs),
attrs: options)
end
def pair([],acc), do: acc
def pair([x],acc), do: [x|acc]
def pair([a,b|t],acc), do: pair(t,[{hd(mapOids([:oid.decode(a)])),b}|acc])
def mapOidsDecode(list) do
:lists.map(fn x ->
:erlang.iolist_to_binary(:string.join(:lists.map(fn y -> :erlang.integer_to_list(y) end,
:erlang.tuple_to_list(:oid.decode(x))),'.')) end, list)
end
def mapOids(list) do
:lists.map(fn x ->
:erlang.iolist_to_binary(:string.join(:lists.map(fn y -> :erlang.integer_to_list(y) end,
:erlang.tuple_to_list(x)),'.')) end, list)
end
def oid({1,3,6,1,5,5,7,1,1}, v), do: {:authorityInfoAccess, pair(v,[])}
def oid({1,3,6,1,5,5,7,1,3}, [v1,v2,url,_lang,v3,v4,v5]), do: {:qcStatements, {url, mapOidsDecode([v1,v2,v3,v4,v5])}}
def oid({1,3,6,1,5,5,7,1,11},v), do: {:subjectInfoAccess, pair(v,[])}
def oid({2,5,29,9},v), do: {:subjectDirectoryAttributes, pair(v,[])}
def oid({2,5,29,14},v), do: {:subjectKeyIdentifier, :base64.encode(hd(pair(v,[])))}
def oid({2,5,29,15},v), do: {:keyUsage, pair(v,[])}
def oid({2,5,29,16},v), do: {:privateKeyUsagePeriod, v}
def oid({2,5,29,17},v), do: {:subjectAltName, pair(v,[])}
def oid({2,5,29,19},v), do: {:basicConstraints, pair(v,[])}
def oid({2,5,29,31},v), do: {:cRLDistributionPoints, pair(v,[])}
def oid({2,5,29,32},[v1,v2,url]), do: {:certificatePolicies, {url, mapOidsDecode([v1,v2])}}
def oid({2,5,29,35},v), do: {:authorityKeyIdentifier, :base64.encode(hd(pair(v,[])))}
def oid({2,5,29,46},v), do: {:freshestCRL, pair(v,[])}
def oid({1,2,840,113549,1,9,3},v), do: {:contentType, hd(mapOidsDecode([v]))}
def oid({1,2,840,113549,1,9,4},v), do: {:messageDigest, :base64.encode(:erlang.element(2,:KEP.decode(:MessageDigest, v)))}
def oid({1,2,840,113549,1,9,5},v), do: {:signingTime, :erlang.element(2,:erlang.element(1,:asn1rt_nif.decode_ber_tlv(v)))}
def oid({1,2,840,113549,1,9,16,2,47},v) do
{:SigningCertificateV2,[{:ESSCertIDv2, _, _, {_,_,serial}}],_} = :erlang.element(2,:KEP.decode(:SigningCertificateV2, v))
{:signingCertificateV2, serial}
end
def oid({1,2,840,113549,1,9,16,2,20},v) do
{:ContentInfo, oid, value} = :erlang.element(2,:KEP.decode(:ContentInfo,v))
{:ok, {:SignedData, _, _alg, {_,_,x}, _c, _x1, _si}} = :KEP.decode(:SignedData, value)
{:ok, {:TSTInfo, _vsn, _oid, {:MessageImprint, _, x}, serial, ts, _,_,_,_}} = :KEP.decode(:TSTInfo, x)
{:contentTimestamp, {hd(mapOids([oid])), serial, :erlang.iolist_to_binary(ts), :base64.encode(x)}}
end
def oid(x,v) when is_binary(x), do: {:oid.decode(x),pair(v,[])}
def oid(x,v), do: {x,v}
def flat(code,{k,v},acc) when is_integer(k), do: [flat(code,v,acc)|acc]
def flat(code,{k,_v},acc), do: [flat(code,k,acc)|acc]
def flat(code,k,acc) when is_list(k), do: [:lists.map(fn x -> flat(code,x,acc) end, k)|acc]
def flat(_code,k,acc) when is_binary(k), do: [k|acc]
def parseCert(cert, [si|_]) do
{:SignerInfo, _v, _serial, _alg, attrs, _, _, _} = si
{:Certificate, a, _, _} = cert
{:Certificate_toBeSigned, _ver, _sel, _alg, issuer, _val, issuee, _a, _b, _c, exts} = a
extensions = :lists.map(fn {:Extension,code,_x,b} ->
oid(code, :lists.flatten(flat(code,:asn1rt_nif.decode_ber_tlv(b),[])))
end, exts)
attributes = :lists.map(fn {:Attribute,code,[{_,v}],_} ->
oid(code, v)
end, attrs)
person = :lists.flatten(:erlang.element(2, issuee))
ca = :lists.flatten(:erlang.element(2, issuer))
[parseAttrs(person,extensions),parseAttrs(ca,attributes)]
end
def parseCert(cert) do
{:Certificate, a, _, _} = cert
{:Certificate_toBeSigned, _ver, _sel, _alg, issuer, _val, issuee, _a, _b, _c, exts} = a
extensions = :lists.map(fn {:Extension,code,_x,b} ->
oid(code, :lists.flatten(flat(code,:asn1rt_nif.decode_ber_tlv(b),[])))
end, exts)
person = :lists.flatten(:erlang.element(2, issuee))
ca = :lists.flatten(:erlang.element(2, issuer))
[parseAttrs(person,extensions),parseAttrs(ca,[])]
end
end