Skip to content
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
ebin
.eunit

.rebar/erlcinfo
9 changes: 5 additions & 4 deletions src/esaml.erl
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,10 @@ to_xml(#esaml_authnreq{version = V, issue_instant = Time, destination = Dest, is
#xmlAttribute{name = 'ProtocolBinding', value = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"}],
content = [
#xmlElement{name = 'saml:Issuer', content = [#xmlText{value = Issuer}]},
#xmlElement{name = 'saml:Subject', content = [
#xmlElement{name = 'saml:SubjectConfirmation', attributes = [#xmlAttribute{name = 'Method', value = "urn:oasis:names:tc:SAML:2.0:cm:bearer"}]}
]}
#xmlElement{name = 'samlp:NameIDPolicy', attributes = [#xmlAttribute{name = 'Format', value = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"}]}
% #xmlElement{name = 'saml:Subject', content = [
% #xmlElement{name = 'saml:SubjectConfirmation', attributes = [#xmlAttribute{name = 'Method', value = "urn:oasis:names:tc:SAML:2.0:cm:bearer"}]}
% ]}
]
});

Expand Down Expand Up @@ -477,7 +478,7 @@ to_xml(#esaml_sp_metadata{org = #esaml_org{name = OrgName, displayname = OrgDisp
content = [#xmlElement{name = 'dsig:X509Data',
content =
[#xmlElement{name = 'dsig:X509Certificate',
content = [#xmlText{value = base64:encode_to_string(CertBin)}]} |
content = [#xmlText{value = base64:encode_to_string(CertBin)}]} |
[#xmlElement{name = 'dsig:X509Certificate',
content = [#xmlText{value = base64:encode_to_string(CertChainBin)}]} || CertChainBin <- CertChain]]}]}]}]
end,
Expand Down
14 changes: 9 additions & 5 deletions src/esaml_binding.erl
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,20 @@ xml_payload_type(Xml) ->
-spec decode_response(SAMLEncoding :: binary(), SAMLResponse :: binary()) -> #xmlDocument{}.
decode_response(?deflate, SAMLResponse) ->
XmlData = binary_to_list(zlib:unzip(base64:decode(SAMLResponse))),
erlang:display(XmlData),
{Xml, _} = xmerl_scan:string(XmlData, [{namespace_conformant, true}]),
Xml;
decode_response(_, SAMLResponse) ->
Data = base64:decode(SAMLResponse),
XmlData = case (catch zlib:unzip(Data)) of
{'EXIT', _} -> binary_to_list(Data);
Bin -> binary_to_list(Bin)
{'EXIT', _} ->
binary_to_list(Data);
Bin ->
binary_to_list(Bin)
end,
{Xml, _} = xmerl_scan:string(XmlData, [{namespace_conformant, true}]),
Xml.
erlang:display(XmlData),
{Xml, _} = xmerl_scan:string(XmlData, [{namespace_conformant, true}]),
Xml.

%% @doc Encode a SAMLRequest (or SAMLResponse) as an HTTP-REDIRECT binding
%%
Expand Down Expand Up @@ -94,4 +98,4 @@ generate_post_html(Type, Dest, Req, RelayState) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").

-endif.
-endif.
11 changes: 8 additions & 3 deletions src/esaml_sp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@
add_xml_id(Xml) ->
Xml#xmlElement{attributes = Xml#xmlElement.attributes ++ [
#xmlAttribute{name = 'ID',
value = uuid:to_string(uuid:uuid1()),
value = "a" ++ uuid:to_string(uuid:uuid1()),
namespace = #xmlNamespace{}}
]}.

%% @doc Return an AuthnRequest as an XML element
-spec generate_authn_request(IdpURL :: string(), esaml:sp()) -> #xmlElement{}.
generate_authn_request(IdpURL, SP = #esaml_sp{metadata_uri = MetaURI, consume_uri = ConsumeURI}) ->
erlang:display("Generate authnreq... v2"),
Now = erlang:localtime_to_universaltime(erlang:localtime()),
Stamp = esaml_util:datetime_to_saml(Now),

Expand Down Expand Up @@ -201,7 +202,11 @@ validate_assertion(Xml, DuplicateFun, SP = #esaml_sp{}) ->
fun(X) ->
case xmerl_xpath:string("/samlp:Response/saml:Assertion", X, [{namespace, Ns}]) of
[A] -> A;
_ -> {error, bad_assertion}
Assertion ->
erlang:display("validating assertion"),
erlang:display(X),
erlang:display(Assertion),
{error, bad_assertion}
end
end,
fun(A) ->
Expand Down Expand Up @@ -240,4 +245,4 @@ validate_assertion(Xml, DuplicateFun, SP = #esaml_sp{}) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").

-endif.
-endif.
12 changes: 11 additions & 1 deletion src/xmerl_c14n.erl
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,10 @@ c14n(#xmlAttribute{nsinfo = NsInfo, name = Name, value = Value}, _KnownNs, Activ

c14n(Elem = #xmlElement{}, KnownNSIn, ActiveNSIn, Comments, InclNs, Acc) ->
Namespace = Elem#xmlElement.namespace,
Default = Namespace#xmlNamespace.default,
Default = case Elem#xmlElement.nsinfo of
[] -> Namespace#xmlNamespace.default;
_ -> [] % omit a default namespace if it is not visibly utilized.
end,
{ActiveNS, ParentDefault} = case ActiveNSIn of
[{default, P} | Rest] -> {Rest, P};
Other -> {Other, ''}
Expand Down Expand Up @@ -356,6 +359,13 @@ default_ns_test() ->

Target2 = "<saml2p:Response xmlns:saml2p=\"urn:oasis:names:tc:SAML:2.0:protocol\" Destination=\"https://10.10.18.25/saml/consume\" ID=\"_83dbf3f1-53c2-4f49-b294-7c19cbf2b77b\" IssueInstant=\"2013-10-30T11:15:47.517Z\" Version=\"2.0\"><Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_debe5f4e-4343-4f95-b997-89db5a483202\" IssueInstant=\"2013-10-30T11:15:47.517Z\" Version=\"2.0\"><Issuer>foo</Issuer><Subject><NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\"></NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData NotOnOrAfter=\"2013-10-30T12:15:47.517Z\" Recipient=\"https://10.10.18.25/saml/consume\"></SubjectConfirmationData></SubjectConfirmation></Subject></Assertion></saml2p:Response>",
Target2 = c14n(Doc2, true).

omit_default_ns_test() ->
{Doc, _} = xmerl_scan:string("<foo:a xmlns:foo=\"urn:foo\"><bar:b xmlns=\"urn:bar\" xmlns:bar=\"urn:bar\"><bar:c /></bar:b></foo:a>", [{namespace_conformant, true}]),

Target = "<foo:a xmlns:foo=\"urn:foo\"><bar:b xmlns:bar=\"urn:bar\"><bar:c></bar:c></bar:b></foo:a>",
Target = c14n(Doc, true).


c14n_inclns_test() ->
{Doc, []} = xmerl_scan:string("<foo:a xmlns:foo=\"urn:foo:\" xmlns:bar=\"urn:bar:\"><foo:b bar:nothing=\"something\">foo</foo:b></foo:a>", [{namespace_conformant, true}]),
Expand Down
20 changes: 15 additions & 5 deletions src/xmerl_dsig.erl
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ sign(ElementIn, PrivateKey = #'RSAPrivateKey'{}, CertBin, SigMethod) when is_bin
case lists:keyfind('id', 2, ElementStrip#xmlElement.attributes) of
#xmlAttribute{value = LowId} -> {ElementStrip, LowId};
_ ->
NewId = uuid:to_string(uuid:uuid1()),
NewId = "a" ++ uuid:to_string(uuid:uuid1()),
Attr = #xmlAttribute{name = 'ID', value = NewId, namespace = #xmlNamespace{}},
NewAttrs = [Attr | ElementStrip#xmlElement.attributes],
Elem = ElementStrip#xmlElement{attributes = NewAttrs},
Expand Down Expand Up @@ -161,11 +161,17 @@ digest(Element, HashFunction) ->
%% Will throw badmatch errors if you give it XML that is not signed
%% according to the xml-dsig spec. If you're using something other
%% than rsa+sha1 or sha256 this will asplode. Don't say I didn't warn you.
-spec verify(Element :: #xmlElement{}, Fingerprints :: [fingerprint()] | any) -> ok | {error, bad_digest | bad_signature | cert_not_accepted}.
verify(Element, Fingerprints) ->
-spec verify(ElementPre :: #xmlElement{}, Fingerprints :: [fingerprint()] | any) -> ok | {error, bad_digest | bad_signature | cert_not_accepted}.
verify(ElementPre, Fingerprints) ->
DsNs = [{"ds", 'http://www.w3.org/2000/09/xmldsig#'},
{"ec", 'http://www.w3.org/2001/10/xml-exc-c14n#'}],

Element = case xmerl_xpath:string("saml2:Assertion", ElementPre, []) of
[] -> case xmerl_xpath:string("Assertion", ElementPre, []) of
[] -> ElementPre;
[Element4 = #xmlElement{}] -> Element4
end;
[Element3 = #xmlElement{}] -> Element3
end,
[#xmlAttribute{value = SignatureMethodAlgorithm}] = xmerl_xpath:string("ds:Signature/ds:SignedInfo/ds:SignatureMethod/@Algorithm", Element, [{namespace, DsNs}]),
{HashFunction, _, _} = signature_props(SignatureMethodAlgorithm),

Expand Down Expand Up @@ -201,7 +207,11 @@ verify(Element, Fingerprints) ->
CertHash2 = crypto:hash(sha256, CertBin),

Cert = public_key:pkix_decode_cert(CertBin, plain),
{_, KeyBin} = Cert#'Certificate'.tbsCertificate#'TBSCertificate'.subjectPublicKeyInfo#'SubjectPublicKeyInfo'.subjectPublicKey,

KeyBin = case Cert#'Certificate'.tbsCertificate#'TBSCertificate'.subjectPublicKeyInfo#'SubjectPublicKeyInfo'.subjectPublicKey of
{_, KeyBin2} -> KeyBin2;
KeyBin3 -> KeyBin3
end,
Key = public_key:pem_entry_decode({'RSAPublicKey', KeyBin, not_encrypted}),

case public_key:verify(Data, HashFunction, Sig, Key) of
Expand Down