diff options
author | Micael Karlberg <[email protected]> | 2011-05-27 14:38:08 +0200 |
---|---|---|
committer | Micael Karlberg <[email protected]> | 2011-05-27 14:38:08 +0200 |
commit | 1c01cf2fb625e105dd2355279635edbb3dbb3063 (patch) | |
tree | 5dbd5a391b86d99c00a01c456f817f30209f1962 /lib/public_key | |
parent | 77fe4062599e4ffe897c21d22ad493cfec4b475c (diff) | |
parent | 4a5a75811e2cd590b5c94f71864a5245fd511ccf (diff) | |
download | otp-1c01cf2fb625e105dd2355279635edbb3dbb3063.tar.gz otp-1c01cf2fb625e105dd2355279635edbb3dbb3063.tar.bz2 otp-1c01cf2fb625e105dd2355279635edbb3dbb3063.zip |
Merge branch 'maint-r14' of super:otp into maint-r14
Diffstat (limited to 'lib/public_key')
32 files changed, 2681 insertions, 550 deletions
diff --git a/lib/public_key/doc/src/Makefile b/lib/public_key/doc/src/Makefile index 298c28a740..afb17399da 100644 --- a/lib/public_key/doc/src/Makefile +++ b/lib/public_key/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2008-2010. All Rights Reserved. +# Copyright Ericsson AB 2008-2011. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -52,6 +52,7 @@ XML_CHAPTER_FILES = \ introduction.xml \ public_key_records.xml \ cert_records.xml \ + using_public_key.xml \ notes.xml BOOK_FILES = book.xml diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml index 14b43041ce..9d77750ea2 100644 --- a/lib/public_key/doc/src/notes.xml +++ b/lib/public_key/doc/src/notes.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2008</year> - <year>2010</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -34,6 +34,22 @@ <file>notes.xml</file> </header> +<section><title>Public_Key 0.12</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The public_key application now supports encode/decode of + ssh public-key files.</p> + <p> + Own Id: OTP-9144</p> + </item> + </list> + </section> + +</section> + <section><title>Public_Key 0.11</title> <section><title>Improvements and New Features</title> diff --git a/lib/public_key/doc/src/part.xml b/lib/public_key/doc/src/part.xml index c338a71613..ea3123b5bd 100644 --- a/lib/public_key/doc/src/part.xml +++ b/lib/public_key/doc/src/part.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE part SYSTEM "part.dtd"> <part xmlns:xi="http://www.w3.org/2001/XInclude"> @@ -38,5 +38,6 @@ <xi:include href="introduction.xml"/> <xi:include href="public_key_records.xml"/> <xi:include href="cert_records.xml"/> + <xi:include href="using_public_key.xml"/> </part> diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index 81aedaea56..d60d91cd83 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -43,7 +43,7 @@ <note><p>All records used in this manual <!-- except #policy_tree_node{} --> - are generated from asn1 specifications + are generated from ASN.1 specifications and are documented in the User's Guide. See <seealso marker="public_key_records">Public key records</seealso> and <seealso marker="cert_records">X.509 Certificate records</seealso>. @@ -56,44 +56,43 @@ <p><em>Data Types </em></p> - <p><c>boolean() = true | false</c></p> + <p><code>boolean() = true | false</code></p> - <p><c>string = [bytes()]</c></p> + <p><code>string = [bytes()]</code></p> - <p><c>der_encoded() = binary() </c></p> - - <p><c>decrypt_der() = binary() </c></p> + <p><code>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey' + 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'</code></p> - <p><c>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey' - 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'</c></p> - - <p><c>pem_entry () = {pki_asn1_type(), der_encoded() | decrypt_der(), not_encrypted | - {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}}.</c></p> - - <p><c>rsa_public_key() = #'RSAPublicKey'{}</c></p> + <p><code>pem_entry () = {pki_asn1_type(), binary() %% DER or encrypted DER + not_encrypted | {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}}.</code></p> - <p><c>rsa_private_key() = #'RSAPrivateKey'{} </c></p> + <p><code>rsa_public_key() = #'RSAPublicKey'{}</code></p> + + <p><code>rsa_private_key() = #'RSAPrivateKey'{} </code></p> - <p><c>dsa_public_key() = {integer(), #'Dss-Parms'{}} </c></p> + <p><code>dsa_public_key() = {integer(), #'Dss-Parms'{}} </code></p> - <p><c>rsa_private_key() = #'RSAPrivateKey'{} </c></p> + <p><code>rsa_private_key() = #'RSAPrivateKey'{} </code></p> - <p><c>dsa_private_key() = #'DSAPrivateKey'{}</c></p> + <p><code>dsa_private_key() = #'DSAPrivateKey'{}</code></p> - <p><c> public_crypt_options() = [{rsa_pad, rsa_padding()}]. </c></p> + <p><code> public_crypt_options() = [{rsa_pad, rsa_padding()}]. </code></p> - <p><c> rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' - | 'rsa_no_padding'</c></p> + <p><code> rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' + | 'rsa_no_padding'</code></p> - <p><c> rsa_digest_type() = 'md5' | 'sha' </c></p> - - <p><c> dss_digest_type() = 'none' | 'sha' </c></p> + <p><code> rsa_digest_type() = 'md5' | 'sha' </code></p> + + <p><code> dss_digest_type() = 'none' | 'sha' </code></p> + + <p><code> ssh_file() = openssh_public_key | rfc4716_public_key | + known_hosts | auth_keys </code></p> -<!-- <p><c>policy_tree() = [Root, Children]</c></p> --> +<!-- <p><code>policy_tree() = [Root, Children]</code></p> --> -<!-- <p><c>Root = #policy_tree_node{}</c></p> --> +<!-- <p><code>Root = #policy_tree_node{}</code></p> --> -<!-- <p><c>Children = [] | policy_tree()</c></p> --> +<!-- <p><code>Children = [] | policy_tree()</code></p> --> <!-- <p> The policy_tree_node record has the following fields:</p> --> @@ -151,12 +150,12 @@ <fsummary> Decodes a public key asn1 der encoded entity.</fsummary> <type> <v>Asn1Type = atom() -</v> - <d> Asn1 type present in the public_key applications + <d> ASN.1 type present in the public_key applications asn1 specifications.</d> <v>Der = der_encoded()</v> </type> <desc> - <p> Decodes a public key asn1 der encoded entity.</p> + <p> Decodes a public key ASN.1 der encoded entity.</p> </desc> </func> @@ -166,25 +165,25 @@ <type> <v>Asn1Type = atom()</v> <d> Asn1 type present in the public_key applications - asn1 specifications.</d> + ASN.1 specifications.</d> <v>Entity = term() - The erlang representation of <c> Asn1Type</c></v> </type> <desc> - <p> Encodes a public key entity with asn1 DER encoding.</p> + <p> Encodes a public key entity with ASN.1 DER encoding.</p> </desc> </func> <func> <name>pem_decode(PemBin) -> [pem_entry()]</name> <fsummary>Decode PEM binary data and return - entries as asn1 der encoded entities. </fsummary> + entries as ASN.1 der encoded entities. </fsummary> <type> <v>PemBin = binary()</v> <d>Example {ok, PemBin} = file:read_file("cert.pem").</d> </type> <desc> <p>Decode PEM binary data and return - entries as asn1 der encoded entities.</p> + entries as ASN.1 der encoded entities.</p> </desc> </func> @@ -259,13 +258,13 @@ <func> <name>pkix_decode_cert(Cert, otp|plain) -> #'Certificate'{} | #'OTPCertificate'{}</name> - <fsummary> Decodes an asn1 der encoded pkix x509 certificate.</fsummary> + <fsummary> Decodes an ASN.1 der encoded pkix x509 certificate.</fsummary> <type> <v>Cert = der_encoded()</v> </type> <desc> - <p>Decodes an asn1 der encoded pkix certificate. The otp option - will use the customized asn1 specification OTP-PKIX.asn1 for + <p>Decodes an ASN.1 der encoded pkix certificate. The otp option + will use the customized ASN.1 specification OTP-PKIX.asn1 for decoding and also recursively decode most of the standard parts.</p> </desc> @@ -277,7 +276,7 @@ certificate.</fsummary> <type> <v>Asn1Type = atom()</v> - <d>The asn1 type can be 'Certificate', 'OTPCertificate' or a subtype of either .</d> + <d>The ASN.1 type can be 'Certificate', 'OTPCertificate' or a subtype of either .</d> </type> <desc> <p>Der encodes a pkix x509 certificate or part of such a @@ -395,7 +394,7 @@ signed or in the case that digest type is <c>none</c> it is the hashed value of "plain text" i.e. the digest.</d> <v>DigestType = rsa_digest_type() | dsa_digest_type()</v> - <v>Key = rsa_public_key() | dsa_public_key()</v> + <v>Key = rsa_private_key() | dsa_private_key()</v> </type> <desc> <p> Creates a digital signature.</p> @@ -403,6 +402,55 @@ </func> <func> + <name>ssh_decode(SshBin, Type) -> [{public_key(), Attributes::list()}]</name> + <fsummary>Decodes a ssh file-binary. </fsummary> + <type> + <v>SshBin = binary()</v> + <d>Example {ok, SshBin} = file:read_file("known_hosts").</d> + <v> Type = public_key | ssh_file()</v> + <d>If <c>Type</c> is <c>public_key</c> the binary may be either + a rfc4716 public key or a openssh public key.</d> + </type> + <desc> + <p> Decodes a ssh file-binary. In the case of know_hosts or + auth_keys the binary may include one or more lines of the + file. Returns a list of public keys and their attributes, possible + attribute values depends on the file type represented by the + binary. + </p> + + <taglist> + <tag>rfc4716 attributes - see RFC 4716</tag> + <item>{headers, [{string(), utf8_string()}]}</item> + <tag>auth_key attributes - see man sshd </tag> + <item>{comment, string()}</item> + <item>{options, [string()]}</item> + <item>{bits, integer()} - In ssh version 1 files</item> + <tag>known_host attributes - see man sshd</tag> + <item>{hostnames, [string()]}</item> + <item>{comment, string()}</item> + <item>{bits, integer()} - In ssh version 1 files</item> + </taglist> + + </desc> + </func> + + <func> + <name>ssh_encode([{Key, Attributes}], Type) -> binary()</name> + <fsummary> Encodes a list of ssh file entries to a binary.</fsummary> + <type> + <v>Key = public_key()</v> + <v>Attributes = list()</v> + <v>Type = ssh_file()</v> + </type> + <desc> + <p>Encodes a list of ssh file entries (public keys and attributes) to a binary. Possible + attributes depends on the file type, see <seealso + marker="ssh_decode"> ssh_decode/2 </seealso></p> + </desc> + </func> + + <func> <name>verify(Msg, DigestType, Signature, Key) -> boolean()</name> <fsummary>Verifies a digital signature.</fsummary> <type> diff --git a/lib/public_key/doc/src/using_public_key.xml b/lib/public_key/doc/src/using_public_key.xml new file mode 100644 index 0000000000..f0eaeb8654 --- /dev/null +++ b/lib/public_key/doc/src/using_public_key.xml @@ -0,0 +1,504 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2011</year><year>2011</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Using the public_key API</title> + <file>using_public_key.xml</file> + </header> + + <section> + <title>General information</title> + + <p> This chapter is dedicated to showing some + examples of how to use the public_key API. Keys and certificates + used in the following sections are generated only for the purpose + of testing the public key application.</p> + + <p>Note that some shell printouts, in the following examples, + have been abbreviated for increased readability.</p> + + </section> + + <section> + <title>PEM files</title> + <p> Pulic key data (keys, certificates etc) may be stored in PEM format. PEM files + comes from the Private Enhanced Mail Internet standard and has a + structure that looks like this:</p> + + <code><text> + -----BEGIN <SOMETHING>----- + <Attribute> : <Value> + <Base64 encoded DER data> + -----END <SOMETHING>----- + <text></code> + + <p>A file can contain several BEGIN/END blocks. Text lines between + blocks are ignored. Attributes, if present, are currently ignored except + for <c>Proc-Type</c> and <c>DEK-Info</c> that are used when the DER data is + encrypted.</p> + + <section> + <title>DSA private key</title> + + <p>Note file handling is not done by the public_key application. </p> + <code>1> {ok, PemBin} = file:read_file("dsa.pem"). +{ok,<<"-----BEGIN DSA PRIVATE KEY-----\nMIIBuw"...>>}</code> + + <p>This PEM file only has one entry a private DSA key.</p> + <code>2> [DSAEntry] = public_key:pem_decode(PemBin). +[{'DSAPrivateKey',<<48,130,1,187,2,1,0,2,129,129,0,183, + 179,230,217,37,99,144,157,21,228,204, + 162,207,61,246,...>>, + not_encrypted}]</code> + + <code>3> Key = public_key:pem_entry_decode(DSAEntry). +#'DSAPrivateKey'{version = 0, + p = 12900045185019966618...6593, + q = 1216700114794736143432235288305776850295620488937, + g = 10442040227452349332...47213, + y = 87256807980030509074...403143, + x = 510968529856012146351317363807366575075645839654}</code> + </section> + + <section> + <title>RSA private key encrypted with a password.</title> + + <code>1> {ok, PemBin} = file:read_file("rsa.pem"). +{ok,<<"Bag Attribut"...>>}</code> + + <p>This PEM file only has one entry a private RSA key.</p> + <code>2>[RSAEntry] = public_key:pem_decode(PemBin). +[{'RSAPrivateKey',<<224,108,117,203,152,40,15,77,128,126, + 221,195,154,249,85,208,202,251,109, + 119,120,57,29,89,19,9,...>>, + {"DES-EDE3-CBC",<<"k�e��p�L">>}}] + + </code> + + <p>In this example the password is "abcd1234".</p> + <code>3> Key = public_key:pem_entry_decode(RSAEntry, "abcd1234"). + #'RSAPrivateKey'{version = 'two-prime', + modulus = 1112355156729921663373...2737107, + publicExponent = 65537, + privateExponent = 58064406231183...2239766033, + prime1 = 11034766614656598484098...7326883017, + prime2 = 10080459293561036618240...77738643771, + exponent1 = 77928819327425934607...22152984217, + exponent2 = 36287623121853605733...20588523793, + coefficient = 924840412626098444...41820968343, + otherPrimeInfos = asn1_NOVALUE}</code> + </section> + + <section> + <title>X509 Certificates</title> + + <code>1> {ok, PemBin} = file:read_file("cacerts.pem"). +{ok,<<"-----BEGIN CERTIFICATE-----\nMIIC7jCCAl"...>>}</code> + + <p>This file includes two certificates</p> + <code>2> [CertEntry1, CertEntry2] = public_key:pem_decode(PemBin). +[{'Certificate',<<48,130,2,238,48,130,2,87,160,3,2,1,2,2, + 9,0,230,145,97,214,191,2,120,150,48,13, + ...>>, + not_encrypted}, + {'Certificate',<<48,130,3,200,48,130,3,49,160,3,2,1,2,2,1, + 1,48,13,6,9,42,134,72,134,247,...>>>, + not_encrypted}]</code> + + <p>Certificates may of course be decoded as usual ... </p> + <code>2> Cert = public_key:pem_entry_decode(CertEntry1). +#'Certificate'{ + tbsCertificate = + #'TBSCertificate'{ + version = v3,serialNumber = 16614168075301976214, + signature = + #'AlgorithmIdentifier'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = <<5,0>>}, + issuer = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = <<19,8,101,114,108,97,110,103,67,65>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = <<19,10,69,114,108,97,110,103,32,79,84,80>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = <<19,11,69,114,105,99,115,115,111,110,32,65,66>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = <<19,9,83,116,111,99,107,104,111,108,109>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,6}, + value = <<19,2,83,69>>}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = <<22,22,112,101,116,101,114,64,101,114,...>>}]]}, + validity = + #'Validity'{ + notBefore = {utcTime,"080109082929Z"}, + notAfter = {utcTime,"080208082929Z"}}, + subject = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = <<19,8,101,114,108,97,110,103,67,65>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = <<19,10,69,114,108,97,110,103,32,79,84,80>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = <<19,11,69,114,105,99,115,115,111,110,32,...>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = <<19,9,83,116,111,99,107,104,111,108,...>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,6}, + value = <<19,2,83,69>>}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = <<22,22,112,101,116,101,114,64,...>>}]]}, + subjectPublicKeyInfo = + #'SubjectPublicKeyInfo'{ + algorithm = + #'AlgorithmIdentifier'{ + algorithm = {1,2,840,113549,1,1,1}, + parameters = <<5,0>>}, + subjectPublicKey = + {0,<<48,129,137,2,129,129,0,203,209,187,77,73,231,90,...>>}}, + issuerUniqueID = asn1_NOVALUE, + subjectUniqueID = asn1_NOVALUE, + extensions = + [#'Extension'{ + extnID = {2,5,29,19}, + critical = true, + extnValue = [48,3,1,1,255]}, + #'Extension'{ + extnID = {2,5,29,15}, + critical = false, + extnValue = [3,2,1,6]}, + #'Extension'{ + extnID = {2,5,29,14}, + critical = false, + extnValue = [4,20,27,217,65,152,6,30,142|...]}, + #'Extension'{ + extnID = {2,5,29,17}, + critical = false, + extnValue = [48,24,129,22,112,101,116,101|...]}]}, + signatureAlgorithm = + #'AlgorithmIdentifier'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = <<5,0>>}, + signature = + {0, + <<163,186,7,163,216,152,63,47,154,234,139,73,154,96,120, + 165,2,52,196,195,109,167,192,...>>}} +</code> + + <p> Parts of certificates can be decoded with + public_key:der_decode/2 using that parts ASN.1 type. + Although application specific certificate + extension requires application specific ASN.1 decode/encode-functions. + Example, the first value of the rdnSequence above is of ASN.1 type + 'X520CommonName'. ({2,5,4,3} = ?id-at-commonName)</p> + + <code>public_key:der_decode('X520CommonName', <<19,8,101,114,108,97,110,103,67,65>>). +{printableString,"erlangCA"}</code> + + <p>... but certificates can also be decode using the pkix_decode_cert/2 that + can customize and recursively decode standard parts of a certificate.</p> + <code>3>{_, DerCert, _} = CertEntry1.</code> + <code>4> public_key:pkix_decode_cert(DerCert, otp). +#'OTPCertificate'{ + tbsCertificate = + #'OTPTBSCertificate'{ + version = v3,serialNumber = 16614168075301976214, + signature = + #'SignatureAlgorithm'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = 'NULL'}, + issuer = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = {printableString,"erlangCA"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = {printableString,"Erlang OTP"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = {printableString,"Ericsson AB"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = {printableString,"Stockholm"}}], + [#'AttributeTypeAndValue'{type = {2,5,4,6},value = "SE"}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = "[email protected]"}]]}, + validity = + #'Validity'{ + notBefore = {utcTime,"080109082929Z"}, + notAfter = {utcTime,"080208082929Z"}}, + subject = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = {printableString,"erlangCA"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = {printableString,"Erlang OTP"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = {printableString,"Ericsson AB"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = {printableString,"Stockholm"}}], + [#'AttributeTypeAndValue'{type = {2,5,4,6},value = "SE"}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = "[email protected]"}]]}, + subjectPublicKeyInfo = + #'OTPSubjectPublicKeyInfo'{ + algorithm = + #'PublicKeyAlgorithm'{ + algorithm = {1,2,840,113549,1,1,1}, + parameters = 'NULL'}, + subjectPublicKey = + #'RSAPublicKey'{ + modulus = + 1431267547247997...37419, + publicExponent = 65537}}, + issuerUniqueID = asn1_NOVALUE, + subjectUniqueID = asn1_NOVALUE, + extensions = + [#'Extension'{ + extnID = {2,5,29,19}, + critical = true, + extnValue = + #'BasicConstraints'{ + cA = true,pathLenConstraint = asn1_NOVALUE}}, + #'Extension'{ + extnID = {2,5,29,15}, + critical = false, + extnValue = [keyCertSign,cRLSign]}, + #'Extension'{ + extnID = {2,5,29,14}, + critical = false, + extnValue = [27,217,65,152,6,30,142,132,245|...]}, + #'Extension'{ + extnID = {2,5,29,17}, + critical = false, + extnValue = [{rfc822Name,"[email protected]"}]}]}, + signatureAlgorithm = + #'SignatureAlgorithm'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = 'NULL'}, + signature = + {0, + <<163,186,7,163,216,152,63,47,154,234,139,73,154,96,120, + 165,2,52,196,195,109,167,192,...>>}} +</code> + + <p>This call is equivalent to public_key:pem_entry_decode(CertEntry1)</p> + <code>5> public_key:pkix_decode_cert(DerCert, plain). +#'Certificate'{ ...} +</code> + </section> + + <section> + <title>Encoding public key data to PEM format</title> + + <p>If you have public key data and and want to create a PEM file + you can do that by calling the functions + public_key:pem_entry_encode/2 and pem_encode/1 and then saving the + result to a file. For example assume you have PubKey = + 'RSAPublicKey'{} then you can create a PEM-"RSA PUBLIC KEY" file + (ASN.1 type 'RSAPublicKey') or a PEM-"PUBLIC KEY" file + ('SubjectPublicKeyInfo' ASN.1 type).</p> + + <p> The second element of the PEM-entry will be the ASN.1 DER encoded + key data.</p> + + <code>1> PemEntry = public_key:pem_entry_encode('RSAPublicKey', RSAPubKey). +{'RSAPublicKey', <<48,72,...>>, not_encrypted} + +2> PemBin = public_key:pem_encode([PemEntry]). +<<"-----BEGIN RSA PUBLIC KEY-----\nMEgC...>> + +3> file:write_file("rsa_pub_key.pem", PemBin). +ok</code> + + <p> or </p> + + <code>1> PemBin = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey). +{'SubjectPublicKeyInfo', <<48,92...>>, not_encrypted} + +2> PemBin = public_key:pem_encode([PemEntry]). +<<"-----BEGIN PUBLIC KEY-----\nMFw...>> + +3> file:write_file("pub_key.pem", PemBin). +ok</code> + + </section> +</section> + +<section> + <title>RSA public key cryptography </title> + <p> Suppose you have PrivateKey = #'RSAPrivateKey{}' and the + plaintext Msg = binary() and the corresponding public key + PublicKey = #'RSAPublicKey'{} then you can do the following. + Note that you normally will only do one of the encrypt or + decrypt operations and the peer will do the other. + </p> + + <p>Encrypt with the private key </p> + <code>RsaEncrypted = public_key:encrypt_private(Msg, PrivateKey), +Msg = public_key:decrypt_public(RsaEncrypted, PublicKey),</code> + + <p>Encrypt with the public key </p> + <code>RsaEncrypted = public_key:encrypt_public(Msg, PublicKey), +Msg = public_key:decrypt_private(RsaEncrypted, PrivateKey),</code> + </section> + + <section> + <title>Digital signatures</title> + + <p> Suppose you have PrivateKey = #'RSAPrivateKey{}'or + #'DSAPrivateKey'{} and the plaintext Msg = binary() and the + corresponding public key PublicKey = #'RSAPublicKey'{} or + {integer(), #'DssParams'{}} then you can do the following. Note + that you normally will only do one of the sign or verify operations + and the peer will do the other. </p> + + <code>Signature = public_key:sign(Msg, sha, PrivateKey), +true = public_key:verify(Msg, sha, Signature, PublicKey),</code> + + <p>It might be appropriate to calculate the message digest before + calling sign or verify and then you can use the none as second + argument.</p> + + <code>Digest = crypto:sha(Msg), +Signature = public_key:sign(Digest, none, PrivateKey), +true = public_key:verify(Digest, none, Signature, PublicKey), + </code> + + </section> + + <section> + <title>SSH files</title> + + <p>SSH typically uses PEM files for private keys but has its + own file format for storing public keys. The erlang public_key + application can be used to parse the content of SSH public key files.</p> + + <section> + <title> RFC 4716 SSH public key files </title> + + <p>RFC 4716 SSH files looks confusingly like PEM files, + but there are some differences.</p> + <code>1> {ok, SshBin} = file:read_file("ssh2_rsa_pub"). +{ok, <<"---- BEGIN SSH2 PUBLIC KEY ----\nAAAA"...>>}</code> + + <p>This is equivalent to calling public_key:ssh_decode(SshBin, rfc4716_public_key). + </p> + <code>2> public_key:ssh_decode(SshBin, public_key). +[{#'RSAPublicKey'{modulus = 794430685...91663, + publicExponent = 35}, []}] +</code> + + </section> + + <section> + <title> Openssh public key format </title> + <code>1> {ok, SshBin} = file:read_file("openssh_dsa_pub"). +{ok,<<"ssh-dss AAAAB3Nza"...>>}</code> + + <p>This is equivalent to calling public_key:ssh_decode(SshBin, openssh_public_key). + </p> + <code>2> public_key:ssh_decode(SshBin, public_key). +[{{15642692...694280725, + #'Dss-Parms'{p = 17291273936...696123221, + q = 1255626590179665817295475654204371833735706001853, + g = 10454211196...480338645}}, + [{comment,"dhopson@VMUbuntu-DSH"}]}] +</code> + </section> + + <section> + <title> Known hosts - openssh format</title> + + <code>1> {ok, SshBin} = file:read_file("known_hosts"). +{ok,<<"hostname.domain.com,192.168.0.1 ssh-rsa AAAAB...>>}</code> + + <p>Returns a list of public keys and their related attributes + each pair of key and attributes corresponds to one entry in + the known hosts file.</p> + + <code>2> public_key:ssh_decode(SshBin, known_hosts). +[{#'RSAPublicKey'{modulus = 1498979460408...72721699, + publicExponent = 35}, + [{hostnames,["hostname.domain.com","192.168.0.1"]}]}, + {#'RSAPublicKey'{modulus = 14989794604088...2721699, + publicExponent = 35}, + [{comment,"[email protected]"}, + {hostnames,["|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA="]}]}] +</code> + </section> + + <section> + <title> Authorized keys - openssh format</title> + + <code>1> {ok, SshBin} = file:read_file("auth_keys"). +{ok, <<"command=\"dump /home\",no-pty,no-port-forwarding ssh-rsa AAA...>>}</code> + + <p>Returns a list of public keys and their related attributes + each pair of key and attributes corresponds to one entry in + the authorized key file.</p> + + <code>2> public_key:ssh_decode(SshBin, auth_keys). +[{#'RSAPublicKey'{modulus = 794430685...691663, + publicExponent = 35}, + [{comment,"dhopson@VMUbuntu-DSH"}, + {options,["command=\"dump/home\"","no-pty", + "no-port-forwarding"]}]}, + {{1564269258491...607694280725, + #'Dss-Parms'{p = 17291273936185...763696123221, + q = 1255626590179665817295475654204371833735706001853, + g = 10454211195705...60511039590076780999046480338645}}, + [{comment,"dhopson@VMUbuntu-DSH"}]}] +</code> + </section> + + <section> + <title> Creating an SSH file from public key data </title> + + <p>If you got a public key <c>PubKey</c> and a related list of + attributes <c>Attributes</c> as returned + by ssh_decode/2 you can create a new ssh file for example</p> + <code>N> SshBin = public_key:ssh_encode([{PubKey, Attributes}], openssh_public_key), +<<"ssh-rsa "...>> +N+1> file:write_file("id_rsa.pub", SshBin). +ok</code> + </section> + </section> +</chapter> diff --git a/lib/public_key/include/public_key.hrl b/lib/public_key/include/public_key.hrl index 3498a2a433..5f97d80f7e 100644 --- a/lib/public_key/include/public_key.hrl +++ b/lib/public_key/include/public_key.hrl @@ -70,14 +70,18 @@ interim_reasons_mask }). - --type der_encoded() :: binary(). --type decrypt_der() :: binary(). +-type public_key() :: rsa_public_key() | dsa_public_key(). +-type rsa_public_key() :: #'RSAPublicKey'{}. +-type rsa_private_key() :: #'RSAPrivateKey'{}. +-type dsa_private_key() :: #'DSAPrivateKey'{}. +-type dsa_public_key() :: {integer(), #'Dss-Parms'{}}. -type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey' | 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'. --type pem_entry() :: {pki_asn1_type(), der_encoded() | decrypt_der(), +-type pem_entry() :: {pki_asn1_type(), binary(), %% DER or Encrypted DER not_encrypted | {Cipher :: string(), Salt :: binary()}}. -type asn1_type() :: atom(). %% see "OTP-PUB-KEY.hrl +-type ssh_file() :: openssh_public_key | rfc4716_public_key | known_hosts | + auth_keys. -endif. % -ifdef(public_key). diff --git a/lib/public_key/src/Makefile b/lib/public_key/src/Makefile index b042b0c30a..5a24b02d2a 100644 --- a/lib/public_key/src/Makefile +++ b/lib/public_key/src/Makefile @@ -41,6 +41,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/public_key-$(VSN) MODULES = \ public_key \ pubkey_pem \ + pubkey_ssh \ pubkey_cert \ pubkey_cert_records diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl index fadb993ed9..5ab9642279 100644 --- a/lib/public_key/src/pubkey_cert.erl +++ b/lib/public_key/src/pubkey_cert.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ %%==================================================================== %%-------------------------------------------------------------------- --spec verify_data(der_encoded()) -> {md5 | sha, binary(), binary()}. +-spec verify_data(DER::binary()) -> {md5 | sha, binary(), binary()}. %% %% Description: Extracts data from DerCert needed to call public_key:verify/4. %%-------------------------------------------------------------------- @@ -146,7 +146,7 @@ validate_issuer(OtpCert, Issuer, UserState, VerifyFun) -> verify_fun(OtpCert, {bad_cert, invalid_issuer}, UserState, VerifyFun) end. %%-------------------------------------------------------------------- --spec validate_signature(#'OTPCertificate'{}, der_encoded(), +-spec validate_signature(#'OTPCertificate'{}, DER::binary(), term(),term(), term(), fun()) -> term(). %% diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl index 2441cfc0e0..b86d7a1f0c 100644 --- a/lib/public_key/src/pubkey_cert_records.erl +++ b/lib/public_key/src/pubkey_cert_records.erl @@ -30,7 +30,7 @@ %%==================================================================== %%-------------------------------------------------------------------- --spec decode_cert(der_encoded()) -> {ok, #'OTPCertificate'{}}. +-spec decode_cert(DerCert::binary()) -> {ok, #'OTPCertificate'{}}. %% %% Description: Recursively decodes a Certificate. %%-------------------------------------------------------------------- diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl index c8c69bcdd0..c26815bc04 100644 --- a/lib/public_key/src/pubkey_pem.erl +++ b/lib/public_key/src/pubkey_pem.erl @@ -69,8 +69,9 @@ encode(PemEntries) -> encode_pem_entries(PemEntries). %%-------------------------------------------------------------------- --spec decipher({pki_asn1_type(), decrypt_der(),{Cipher :: string(), Salt :: binary()}}, string()) -> - der_encoded(). +-spec decipher({pki_asn1_type(), DerEncrypted::binary(),{Cipher :: string(), + Salt :: binary()}}, + string()) -> Der::binary(). %% %% Description: Deciphers a decrypted pem entry. %%-------------------------------------------------------------------- @@ -78,7 +79,8 @@ decipher({_, DecryptDer, {Cipher,Salt}}, Password) -> decode_key(DecryptDer, Password, Cipher, Salt). %%-------------------------------------------------------------------- --spec cipher(der_encoded(),{Cipher :: string(), Salt :: binary()} , string()) -> binary(). +-spec cipher(Der::binary(),{Cipher :: string(), Salt :: binary()} , + string()) -> binary(). %% %% Description: Ciphers a PEM entry %%-------------------------------------------------------------------- @@ -91,11 +93,11 @@ cipher(Der, {Cipher,Salt}, Password)-> encode_pem_entries(Entries) -> [encode_pem_entry(Entry) || Entry <- Entries]. -encode_pem_entry({Asn1Type, Der, not_encrypted}) -> - StartStr = pem_start(Asn1Type), +encode_pem_entry({Type, Der, not_encrypted}) -> + StartStr = pem_start(Type), [StartStr, "\n", b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"]; -encode_pem_entry({Asn1Type, Der, {Cipher, Salt}}) -> - StartStr = pem_start(Asn1Type), +encode_pem_entry({Type, Der, {Cipher, Salt}}) -> + StartStr = pem_start(Type), [StartStr,"\n", pem_decrypt(),"\n", pem_decrypt_info(Cipher, Salt),"\n", b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"]. @@ -115,17 +117,17 @@ decode_pem_entries([Start| Lines], Entries) -> end. decode_pem_entry(Start, [<<"Proc-Type: 4,ENCRYPTED", _/binary>>, Line | Lines]) -> - Asn1Type = asn1_type(Start), + Type = asn1_type(Start), Cs = erlang:iolist_to_binary(Lines), Decoded = base64:mime_decode(Cs), [_, DekInfo0] = string:tokens(binary_to_list(Line), ": "), [Cipher, Salt] = string:tokens(DekInfo0, ","), - {Asn1Type, Decoded, {Cipher, unhex(Salt)}}; + {Type, Decoded, {Cipher, unhex(Salt)}}; decode_pem_entry(Start, Lines) -> - Asn1Type = asn1_type(Start), + Type = asn1_type(Start), Cs = erlang:iolist_to_binary(Lines), - Der = base64:mime_decode(Cs), - {Asn1Type, Der, not_encrypted}. + Decoded = base64:mime_decode(Cs), + {Type, Decoded, not_encrypted}. split_bin(Bin) -> split_bin(0, Bin). @@ -153,17 +155,7 @@ split_lines(Bin) -> [Bin]. %% Ignore white space at end of line -join_entry([<<"-----END CERTIFICATE-----", _/binary>>| Lines], Entry) -> - {lists:reverse(Entry), Lines}; -join_entry([<<"-----END RSA PRIVATE KEY-----", _/binary>>| Lines], Entry) -> - {lists:reverse(Entry), Lines}; -join_entry([<<"-----END PUBLIC KEY-----", _/binary>>| Lines], Entry) -> - {lists:reverse(Entry), Lines}; -join_entry([<<"-----END RSA PUBLIC KEY-----", _/binary>>| Lines], Entry) -> - {lists:reverse(Entry), Lines}; -join_entry([<<"-----END DSA PRIVATE KEY-----", _/binary>>| Lines], Entry) -> - {lists:reverse(Entry), Lines}; -join_entry([<<"-----END DH PARAMETERS-----", _/binary>>| Lines], Entry) -> +join_entry([<<"-----END ", _/binary>>| Lines], Entry) -> {lists:reverse(Entry), Lines}; join_entry([Line | Lines], Entry) -> join_entry(Lines, [Line | Entry]). diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl new file mode 100644 index 0000000000..f342eab159 --- /dev/null +++ b/lib/public_key/src/pubkey_ssh.erl @@ -0,0 +1,431 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(pubkey_ssh). + +-include("public_key.hrl"). + +-export([decode/2, encode/2]). + +-define(UINT32(X), X:32/unsigned-big-integer). +%% Max encoded line length is 72, but conformance examples use 68 +%% Comment from rfc 4716: "The following are some examples of public +%% key files that are compliant (note that the examples all wrap +%% before 72 bytes to meet IETF document requirements; however, they +%% are still compliant.)" So we choose to use 68 also. +-define(ENCODED_LINE_LENGTH, 68). + +%%==================================================================== +%% Internal application API +%%==================================================================== + +%%-------------------------------------------------------------------- +-spec decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}]. +%% +%% Description: Decodes a ssh file-binary. +%%-------------------------------------------------------------------- +decode(Bin, public_key)-> + case binary:match(Bin, begin_marker()) of + nomatch -> + openssh_decode(Bin, openssh_public_key); + _ -> + rfc4716_decode(Bin) + end; +decode(Bin, rfc4716_public_key) -> + rfc4716_decode(Bin); +decode(Bin, Type) -> + openssh_decode(Bin, Type). + +%%-------------------------------------------------------------------- +-spec encode([{public_key(), Attributes::list()}], ssh_file()) -> + binary(). +%% +%% Description: Encodes a list of ssh file entries. +%%-------------------------------------------------------------------- +encode(Entries, Type) -> + erlang:iolist_to_binary(lists:map(fun({Key, Attributes}) -> + do_encode(Type, Key, Attributes) + end, Entries)). + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +begin_marker() -> + <<"---- BEGIN SSH2 PUBLIC KEY ----">>. +end_marker() -> + <<"---- END SSH2 PUBLIC KEY ----">>. + +rfc4716_decode(Bin) -> + Lines = binary:split(Bin, <<"\n">>, [global]), + do_rfc4716_decode(Lines, []). + +do_rfc4716_decode([<<"---- BEGIN SSH2 PUBLIC KEY ----", _/binary>> | Lines], Acc) -> + do_rfc4716_decode(Lines, Acc); +%% Ignore empty lines before or after begin/end - markers. +do_rfc4716_decode([<<>> | Lines], Acc) -> + do_rfc4716_decode(Lines, Acc); +do_rfc4716_decode([], Acc) -> + lists:reverse(Acc); +do_rfc4716_decode(Lines, Acc) -> + {Headers, PubKey, Rest} = rfc4716_decode_lines(Lines, []), + case Headers of + [_|_] -> + do_rfc4716_decode(Rest, [{PubKey, [{headers, Headers}]} | Acc]); + _ -> + do_rfc4716_decode(Rest, [{PubKey, []} | Acc]) + end. + +rfc4716_decode_lines([Line | Lines], Acc) -> + case binary:last(Line) of + $\\ -> + NewLine = binary:replace(Line,<<"\\">>, hd(Lines), []), + rfc4716_decode_lines([NewLine | tl(Lines)], Acc); + _ -> + rfc4716_decode_line(Line, Lines, Acc) + end. + +rfc4716_decode_line(Line, Lines, Acc) -> + case binary:split(Line, <<":">>) of + [Tag, Value] -> + rfc4716_decode_lines(Lines, [{string_decode(Tag), unicode_decode(Value)} | Acc]); + _ -> + {Body, Rest} = join_entry([Line | Lines], []), + {lists:reverse(Acc), rfc4716_pubkey_decode(base64:mime_decode(Body)), Rest} + end. + +join_entry([<<"---- END SSH2 PUBLIC KEY ----", _/binary>>| Lines], Entry) -> + {lists:reverse(Entry), Lines}; +join_entry([Line | Lines], Entry) -> + join_entry(Lines, [Line | Entry]). + + +rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary, + ?UINT32(SizeE), E:SizeE/binary, + ?UINT32(SizeN), N:SizeN/binary>>) when Type == <<"ssh-rsa">> -> + #'RSAPublicKey'{modulus = erlint(SizeN, N), + publicExponent = erlint(SizeE, E)}; + +rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary, + ?UINT32(SizeP), P:SizeP/binary, + ?UINT32(SizeQ), Q:SizeQ/binary, + ?UINT32(SizeG), G:SizeG/binary, + ?UINT32(SizeY), Y:SizeY/binary>>) when Type == <<"ssh-dss">> -> + {erlint(SizeY, Y), + #'Dss-Parms'{p = erlint(SizeP, P), + q = erlint(SizeQ, Q), + g = erlint(SizeG, G)}}. + +openssh_decode(Bin, FileType) -> + Lines = binary:split(Bin, <<"\n">>, [global]), + do_openssh_decode(FileType, Lines, []). + +do_openssh_decode(_, [], Acc) -> + lists:reverse(Acc); +%% Ignore empty lines +do_openssh_decode(FileType, [<<>> | Lines], Acc) -> + do_openssh_decode(FileType, Lines, Acc); +%% Ignore lines that start with # +do_openssh_decode(FileType,[<<"#", _/binary>> | Lines], Acc) -> + do_openssh_decode(FileType, Lines, Acc); +do_openssh_decode(auth_keys = FileType, [Line | Lines], Acc) -> + Split = binary:split(Line, <<" ">>, [global]), + case mend_split(Split, []) of + %% ssh2 + [Options, KeyType, Base64Enc, Comment] when KeyType == <<"ssh-rsa">>; + KeyType == <<"ssh-dss">> -> + do_openssh_decode(FileType, Lines, + [{openssh_pubkey_decode(KeyType, Base64Enc), + [{comment, string_decode(Comment)}, + {options, comma_list_decode(Options)}]} + | Acc]); + + [KeyType, Base64Enc, Comment] when KeyType == <<"ssh-rsa">>; + KeyType == <<"ssh-dss">> -> + do_openssh_decode(FileType, Lines, + [{openssh_pubkey_decode(KeyType, Base64Enc), + [{comment, string_decode(Comment)}]} | Acc]); + %% ssh1 + [Options, Bits, Exponent, Modulus, Comment] -> + do_openssh_decode(FileType, Lines, + [{ssh1_rsa_pubkey_decode(Modulus, Exponent), + [{comment, string_decode(Comment)}, + {options, comma_list_decode(Options)}, + {bits, integer_decode(Bits)}]} | Acc]); + [Bits, Exponent, Modulus, Comment] -> + do_openssh_decode(FileType, Lines, + [{ssh1_rsa_pubkey_decode(Modulus, Exponent), + [{comment, string_decode(Comment)}, + {bits, integer_decode(Bits)}]} | Acc]) + end; + +do_openssh_decode(known_hosts = FileType, [Line | Lines], Acc) -> + case binary:split(Line, <<" ">>, [global]) of + %% ssh 2 + [HostNames, KeyType, Base64Enc] when KeyType == <<"ssh-rsa">>; + KeyType == <<"ssh-dss">> -> + do_openssh_decode(FileType, Lines, + [{openssh_pubkey_decode(KeyType, Base64Enc), + [{hostnames, comma_list_decode(HostNames)}]}| Acc]); + [HostNames, KeyType, Base64Enc, Comment] when KeyType == <<"ssh-rsa">>; + KeyType == <<"ssh-dss">> -> + do_openssh_decode(FileType, Lines, + [{openssh_pubkey_decode(KeyType, Base64Enc), + [{comment, string_decode(Comment)}, + {hostnames, comma_list_decode(HostNames)}]} | Acc]); + %% ssh 1 + [HostNames, Bits, Exponent, Modulus, Comment] -> + do_openssh_decode(FileType, Lines, + [{ssh1_rsa_pubkey_decode(Modulus, Exponent), + [{comment, string_decode(Comment)}, + {hostnames, comma_list_decode(HostNames)}, + {bits, integer_decode(Bits)}]} | Acc]); + [HostNames, Bits, Exponent, Modulus] -> + do_openssh_decode(FileType, Lines, + [{ssh1_rsa_pubkey_decode(Modulus, Exponent), + [{comment, []}, + {hostnames, comma_list_decode(HostNames)}, + {bits, integer_decode(Bits)}]} | Acc]) + end; + +do_openssh_decode(openssh_public_key = FileType, [Line | Lines], Acc) -> + case binary:split(Line, <<" ">>, [global]) of + [KeyType, Base64Enc, Comment0] when KeyType == <<"ssh-rsa">>; + KeyType == <<"ssh-dss">> -> + Comment = string:strip(binary_to_list(Comment0), right, $\n), + do_openssh_decode(FileType, Lines, + [{openssh_pubkey_decode(KeyType, Base64Enc), + [{comment, Comment}]} | Acc]) + end. + + +openssh_pubkey_decode(<<"ssh-rsa">>, Base64Enc) -> + <<?UINT32(StrLen), _:StrLen/binary, + ?UINT32(SizeE), E:SizeE/binary, + ?UINT32(SizeN), N:SizeN/binary>> + = base64:mime_decode(Base64Enc), + #'RSAPublicKey'{modulus = erlint(SizeN, N), + publicExponent = erlint(SizeE, E)}; + +openssh_pubkey_decode(<<"ssh-dss">>, Base64Enc) -> + <<?UINT32(StrLen), _:StrLen/binary, + ?UINT32(SizeP), P:SizeP/binary, + ?UINT32(SizeQ), Q:SizeQ/binary, + ?UINT32(SizeG), G:SizeG/binary, + ?UINT32(SizeY), Y:SizeY/binary>> + = base64:mime_decode(Base64Enc), + {erlint(SizeY, Y), + #'Dss-Parms'{p = erlint(SizeP, P), + q = erlint(SizeQ, Q), + g = erlint(SizeG, G)}}. + +erlint(MPIntSize, MPIntValue) -> + Bits= MPIntSize * 8, + <<Integer:Bits/integer>> = MPIntValue, + Integer. + +ssh1_rsa_pubkey_decode(MBin, EBin) -> + #'RSAPublicKey'{modulus = integer_decode(MBin), + publicExponent = integer_decode(EBin)}. + +integer_decode(BinStr) -> + list_to_integer(binary_to_list(BinStr)). + +string_decode(BinStr) -> + binary_to_list(BinStr). + +unicode_decode(BinStr) -> + unicode:characters_to_list(BinStr). + +comma_list_decode(BinOpts) -> + CommaList = binary:split(BinOpts, <<",">>, [global]), + lists:map(fun(Item) -> + binary_to_list(Item) + end, CommaList). + +do_encode(rfc4716_public_key, Key, Attributes) -> + rfc4716_encode(Key, proplists:get_value(headers, Attributes, []), []); + +do_encode(Type, Key, Attributes) -> + openssh_encode(Type, Key, Attributes). + +rfc4716_encode(Key, [],[]) -> + erlang:iolist_to_binary([begin_marker(),"\n", + split_lines(base64:encode(ssh2_pubkey_encode(Key))), + "\n", end_marker(), "\n"]); +rfc4716_encode(Key, [], [_|_] = Acc) -> + erlang:iolist_to_binary([begin_marker(), "\n", + lists:reverse(Acc), + split_lines(base64:encode(ssh2_pubkey_encode(Key))), + "\n", end_marker(), "\n"]); +rfc4716_encode(Key, [ Header | Headers], Acc) -> + LinesStr = rfc4716_encode_header(Header), + rfc4716_encode(Key, Headers, [LinesStr | Acc]). + +rfc4716_encode_header({Tag, Value}) -> + TagLen = length(Tag), + ValueLen = length(Value), + case TagLen + 1 + ValueLen of + N when N > ?ENCODED_LINE_LENGTH -> + NumOfChars = ?ENCODED_LINE_LENGTH - (TagLen + 1), + {First, Rest} = lists:split(NumOfChars, Value), + [Tag,":" , First, [$\\], "\n", rfc4716_encode_value(Rest) , "\n"]; + _ -> + [Tag, ":", Value, "\n"] + end. + +rfc4716_encode_value(Value) -> + case length(Value) of + N when N > ?ENCODED_LINE_LENGTH -> + {First, Rest} = lists:split(?ENCODED_LINE_LENGTH, Value), + [First, [$\\], "\n", rfc4716_encode_value(Rest)]; + _ -> + Value + end. + +openssh_encode(openssh_public_key, Key, Attributes) -> + Comment = proplists:get_value(comment, Attributes), + Enc = base64:encode(ssh2_pubkey_encode(Key)), + erlang:iolist_to_binary([key_type(Key), " ", Enc, " ", Comment, "\n"]); + +openssh_encode(auth_keys, Key, Attributes) -> + Comment = proplists:get_value(comment, Attributes, ""), + Options = proplists:get_value(options, Attributes, undefined), + Bits = proplists:get_value(bits, Attributes, undefined), + case Bits of + undefined -> + openssh_ssh2_auth_keys_encode(Options, Key, Comment); + _ -> + openssh_ssh1_auth_keys_encode(Options, Bits, Key, Comment) + end; +openssh_encode(known_hosts, Key, Attributes) -> + Comment = proplists:get_value(comment, Attributes, ""), + Hostnames = proplists:get_value(hostnames, Attributes), + Bits = proplists:get_value(bits, Attributes, undefined), + case Bits of + undefined -> + openssh_ssh2_know_hosts_encode(Hostnames, Key, Comment); + _ -> + openssh_ssh1_known_hosts_encode(Hostnames, Bits, Key, Comment) + end. + +openssh_ssh2_auth_keys_encode(undefined, Key, Comment) -> + erlang:iolist_to_binary([key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]); +openssh_ssh2_auth_keys_encode(Options, Key, Comment) -> + erlang:iolist_to_binary([comma_list_encode(Options, []), " ", + key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]). + +openssh_ssh1_auth_keys_encode(undefined, Bits, + #'RSAPublicKey'{modulus = N, publicExponent = E}, + Comment) -> + erlang:iolist_to_binary([integer_to_list(Bits), " ", integer_to_list(E), " ", integer_to_list(N), + line_end(Comment)]); +openssh_ssh1_auth_keys_encode(Options, Bits, + #'RSAPublicKey'{modulus = N, publicExponent = E}, + Comment) -> + erlang:iolist_to_binary([comma_list_encode(Options, []), " ", integer_to_list(Bits), + " ", integer_to_list(E), " ", integer_to_list(N), line_end(Comment)]). + +openssh_ssh2_know_hosts_encode(Hostnames, Key, Comment) -> + erlang:iolist_to_binary([comma_list_encode(Hostnames, []), " ", + key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]). + +openssh_ssh1_known_hosts_encode(Hostnames, Bits, + #'RSAPublicKey'{modulus = N, publicExponent = E}, + Comment) -> + erlang:iolist_to_binary([comma_list_encode(Hostnames, [])," ", integer_to_list(Bits)," ", + integer_to_list(E)," ", integer_to_list(N), line_end(Comment)]). + +line_end("") -> + "\n"; +line_end(Comment) -> + [" ", Comment, "\n"]. + +key_type(#'RSAPublicKey'{}) -> + <<"ssh-rsa">>; +key_type({_, #'Dss-Parms'{}}) -> + <<"ssh-dss">>. + +comma_list_encode([Option], []) -> + Option; +comma_list_encode([Option], Acc) -> + Acc ++ "," ++ Option; +comma_list_encode([Option | Rest], []) -> + comma_list_encode(Rest, Option); +comma_list_encode([Option | Rest], Acc) -> + comma_list_encode(Rest, Acc ++ "," ++ Option). + +ssh2_pubkey_encode(#'RSAPublicKey'{modulus = N, publicExponent = E}) -> + TypeStr = <<"ssh-rsa">>, + StrLen = size(TypeStr), + EBin = crypto:mpint(E), + NBin = crypto:mpint(N), + <<?UINT32(StrLen), TypeStr:StrLen/binary, + EBin/binary, + NBin/binary>>; +ssh2_pubkey_encode({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) -> + TypeStr = <<"ssh-dss">>, + StrLen = size(TypeStr), + PBin = crypto:mpint(P), + QBin = crypto:mpint(Q), + GBin = crypto:mpint(G), + YBin = crypto:mpint(Y), + <<?UINT32(StrLen), TypeStr:StrLen/binary, + PBin/binary, + QBin/binary, + GBin/binary, + YBin/binary>>. + +mend_split([Part1, Part2 | Rest] = List, Acc) -> + case option_end(Part1, Part2) of + true -> + lists:reverse(Acc) ++ List; + false -> + case length(binary:matches(Part1, <<"\"">>)) of + N when N rem 2 == 0 -> + mend_split(Rest, [Part1 | Acc]); + _ -> + mend_split([<<Part1/binary, Part2/binary>> | Rest], Acc) + end + end. + +option_end(Part1, Part2) -> + (is_key_field(Part1) orelse is_bits_field(Part1)) + orelse + (is_key_field(Part2) orelse is_bits_field(Part2)). + +is_key_field(<<"ssh-dss">>) -> + true; +is_key_field(<<"ssh-rsa">>) -> + true; +is_key_field(_) -> + false. + +is_bits_field(Part) -> + try list_to_integer(binary_to_list(Part)) of + _ -> + true + catch _:_ -> + false + end. + +split_lines(<<Text:?ENCODED_LINE_LENGTH/binary>>) -> + [Text]; +split_lines(<<Text:?ENCODED_LINE_LENGTH/binary, Rest/binary>>) -> + [Text, $\n | split_lines(Rest)]; +split_lines(Bin) -> + [Bin]. diff --git a/lib/public_key/src/public_key.app.src b/lib/public_key/src/public_key.app.src index 60487946fa..1963bd05d4 100644 --- a/lib/public_key/src/public_key.app.src +++ b/lib/public_key/src/public_key.app.src @@ -1,9 +1,9 @@ {application, public_key, [{description, "Public key infrastructure"}, {vsn, "%VSN%"}, - {modules, [ - public_key, - pubkey_pem, + {modules, [ public_key, + pubkey_pem, + pubkey_ssh, pubkey_cert, pubkey_cert_records, 'OTP-PUB-KEY' diff --git a/lib/public_key/src/public_key.appup.src b/lib/public_key/src/public_key.appup.src index c65ac7bc99..4986801dad 100644 --- a/lib/public_key/src/public_key.appup.src +++ b/lib/public_key/src/public_key.appup.src @@ -1,6 +1,16 @@ %% -*- erlang -*- {"%VSN%", [ + {"0.11", + [ + {update, public_key, soft, soft_purge, soft_purge, []}, + {update, pubkey_pem, soft, soft_purge, soft_purge, []}, + {add_module, pubkey_ssh, soft, soft_purge, soft_purge}, + {update, pubkey_cert, soft, soft_purge, soft_purge, []}, + {update, pubkey_cert_records, soft, soft_purge, soft_purge, []} + ] + }, + {"0.10", [ {update, public_key, soft, soft_purge, soft_purge, []}, @@ -25,6 +35,16 @@ } ], [ + {"0.11", + [ + {update, public_key, soft, soft_purge, soft_purge, []}, + {update, pubkey_pem, soft, soft_purge, soft_purge, []}, + {delete_module, pubkey_ssh, soft, soft_purge, soft_purge}, + {update, pubkey_cert, soft, soft_purge, soft_purge, []}, + {update, pubkey_cert_records, soft, soft_purge, soft_purge, []} + ] + }, + {"0.10", [ {update, public_key, soft, soft_purge, soft_purge, []}, diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 7e022da7e5..2901020e83 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -41,7 +41,8 @@ pkix_is_issuer/2, pkix_issuer_id/2, pkix_normalize_name/1, - pkix_path_validation/3 + pkix_path_validation/3, + ssh_decode/2, ssh_encode/2 ]). %% Deprecated @@ -51,10 +52,6 @@ -deprecated({decode_private_key, 1, next_major_release}). -deprecated({decode_private_key, 2, next_major_release}). --type rsa_public_key() :: #'RSAPublicKey'{}. --type rsa_private_key() :: #'RSAPrivateKey'{}. --type dsa_private_key() :: #'DSAPrivateKey'{}. --type dsa_public_key() :: {integer(), #'Dss-Parms'{}}. -type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' | 'rsa_no_padding'. -type public_crypt_options() :: [{rsa_pad, rsa_padding()}]. @@ -67,7 +64,6 @@ %%==================================================================== %% API %%==================================================================== - %%-------------------------------------------------------------------- -spec pem_decode(binary()) -> [pem_entry()]. %% @@ -152,7 +148,7 @@ pem_entry_encode(Asn1Type, Entity, {Asn1Type, DecryptDer, CipherInfo}. %%-------------------------------------------------------------------- --spec der_decode(asn1_type(), der_encoded()) -> term(). +-spec der_decode(asn1_type(), Der::binary()) -> term(). %% %% Description: Decodes a public key asn1 der encoded entity. %%-------------------------------------------------------------------- @@ -166,7 +162,7 @@ der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) -> end. %%-------------------------------------------------------------------- --spec der_encode(asn1_type(), term()) -> der_encoded(). +-spec der_encode(asn1_type(), term()) -> Der::binary(). %% %% Description: Encodes a public key entity with asn1 DER encoding. %%-------------------------------------------------------------------- @@ -180,7 +176,7 @@ der_encode(Asn1Type, Entity) when is_atom(Asn1Type) -> end. %%-------------------------------------------------------------------- --spec pkix_decode_cert(der_encoded(), plain | otp) -> +-spec pkix_decode_cert(Cert::binary(), plain | otp) -> #'Certificate'{} | #'OTPCertificate'{}. %% %% Description: Decodes an asn1 der encoded pkix certificate. The otp @@ -201,7 +197,7 @@ pkix_decode_cert(DerCert, otp) when is_binary(DerCert) -> end. %%-------------------------------------------------------------------- --spec pkix_encode(asn1_type(), term(), otp | plain) -> der_encoded(). +-spec pkix_encode(asn1_type(), term(), otp | plain) -> Der::binary(). %% %% Description: Der encodes a certificate or part of a certificate. %% This function must be used for encoding certificates or parts of certificates @@ -361,7 +357,7 @@ verify(PlainText, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) crypto:mpint(G), crypto:mpint(Key)]). %%-------------------------------------------------------------------- -spec pkix_sign(#'OTPTBSCertificate'{}, - rsa_private_key() | dsa_private_key()) -> der_encoded(). + rsa_private_key() | dsa_private_key()) -> Der::binary(). %% %% Description: Sign a pkix x.509 certificate. Returns the corresponding %% der encoded 'Certificate'{} @@ -370,7 +366,7 @@ pkix_sign(#'OTPTBSCertificate'{signature = #'SignatureAlgorithm'{algorithm = Alg} = SigAlg} = TBSCert, Key) -> - Msg = pkix_encode('OTPTBSCertificate', TBSCert, otp), + Msg = pkix_encode('OTPTBSCertificate', TBSCert, otp), DigestType = pubkey_cert:digest_type(Alg), Signature = sign(Msg, DigestType, Key), Cert = #'OTPCertificate'{tbsCertificate= TBSCert, @@ -380,7 +376,7 @@ pkix_sign(#'OTPTBSCertificate'{signature = pkix_encode('OTPCertificate', Cert, otp). %%-------------------------------------------------------------------- --spec pkix_verify(der_encoded(), rsa_public_key()| +-spec pkix_verify(Cert::binary(), rsa_public_key()| dsa_public_key()) -> boolean(). %% %% Description: Verify pkix x.509 certificate signature. @@ -396,9 +392,9 @@ pkix_verify(DerCert, #'RSAPublicKey'{} = RSAKey) verify(PlainText, DigestType, Signature, RSAKey). %%-------------------------------------------------------------------- --spec pkix_is_issuer(Cert :: der_encoded()| #'OTPCertificate'{}, - IssuerCert :: der_encoded()| - #'OTPCertificate'{}) -> boolean(). +-spec pkix_is_issuer(Cert::binary()| #'OTPCertificate'{}, + IssuerCert::binary()| + #'OTPCertificate'{}) -> boolean(). %% %% Description: Checks if <IssuerCert> issued <Cert>. %%-------------------------------------------------------------------- @@ -414,7 +410,7 @@ pkix_is_issuer(#'OTPCertificate'{tbsCertificate = TBSCert}, Candidate#'OTPTBSCertificate'.subject). %%-------------------------------------------------------------------- --spec pkix_is_self_signed(der_encoded()| #'OTPCertificate'{}) -> boolean(). +-spec pkix_is_self_signed(Cert::binary()| #'OTPCertificate'{}) -> boolean(). %% %% Description: Checks if a Certificate is self signed. %%-------------------------------------------------------------------- @@ -425,7 +421,7 @@ pkix_is_self_signed(Cert) when is_binary(Cert) -> pkix_is_self_signed(OtpCert). %%-------------------------------------------------------------------- --spec pkix_is_fixed_dh_cert(der_encoded()| #'OTPCertificate'{}) -> boolean(). +-spec pkix_is_fixed_dh_cert(Cert::binary()| #'OTPCertificate'{}) -> boolean(). %% %% Description: Checks if a Certificate is a fixed Diffie-Hellman Cert. %%-------------------------------------------------------------------- @@ -436,14 +432,14 @@ pkix_is_fixed_dh_cert(Cert) when is_binary(Cert) -> pkix_is_fixed_dh_cert(OtpCert). %%-------------------------------------------------------------------- --spec pkix_issuer_id(der_encoded()| #'OTPCertificate'{}, - IssuedBy :: self | other) -> - {ok, {SerialNr :: integer(), - Issuer :: {rdnSequence, - [#'AttributeTypeAndValue'{}]}}} +-spec pkix_issuer_id(Cert::binary()| #'OTPCertificate'{}, + IssuedBy :: self | other) -> + {ok, {SerialNr :: integer(), + Issuer :: {rdnSequence, + [#'AttributeTypeAndValue'{}]}}} | {error, Reason :: term()}. % -%% Description: Returns the issuer id. +%% Description: Returns the issuer id. %%-------------------------------------------------------------------- pkix_issuer_id(#'OTPCertificate'{} = OtpCert, self) -> pubkey_cert:issuer_id(OtpCert, self); @@ -456,8 +452,8 @@ pkix_issuer_id(Cert, Signed) when is_binary(Cert) -> pkix_issuer_id(OtpCert, Signed). %%-------------------------------------------------------------------- --spec pkix_normalize_name({rdnSequence, - [#'AttributeTypeAndValue'{}]}) -> +-spec pkix_normalize_name({rdnSequence, + [#'AttributeTypeAndValue'{}]}) -> {rdnSequence, [#'AttributeTypeAndValue'{}]}. %% @@ -468,8 +464,8 @@ pkix_normalize_name(Issuer) -> pubkey_cert:normalize_general_name(Issuer). %%-------------------------------------------------------------------- --spec pkix_path_validation(der_encoded()| #'OTPCertificate'{} | atom(), - CertChain :: [der_encoded()] , +-spec pkix_path_validation(Cert::binary()| #'OTPCertificate'{} | atom(), + CertChain :: [binary()] , Options :: list()) -> {ok, {PublicKeyInfo :: term(), PolicyTree :: term()}} | @@ -496,7 +492,7 @@ pkix_path_validation(TrustedCert, CertChain, Options) when is_binary(TrustedCert) -> OtpCert = pkix_decode_cert(TrustedCert, otp), pkix_path_validation(OtpCert, CertChain, Options); -pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options) +pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options) when is_list(CertChain), is_list(Options) -> MaxPathDefault = length(CertChain), ValidationState = pubkey_cert:init_validation_state(TrustedCert, @@ -505,6 +501,37 @@ pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options) path_validation(CertChain, ValidationState). %%-------------------------------------------------------------------- +-spec ssh_decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}]. +%% +%% Description: Decodes a ssh file-binary. In the case of know_hosts +%% or auth_keys the binary may include one or more lines of the +%% file. Returns a list of public keys and their attributes, possible +%% attribute values depends on the file type represented by the +%% binary. +%%-------------------------------------------------------------------- +ssh_decode(SshBin, Type) when is_binary(SshBin), + Type == public_key; + Type == rfc4716_public_key; + Type == openssh_public_key; + Type == auth_keys; + Type == known_hosts -> + pubkey_ssh:decode(SshBin, Type). + +%%-------------------------------------------------------------------- +-spec ssh_encode([{public_key(), Attributes::list()}], ssh_file()) -> + binary(). +%% Description: Encodes a list of ssh file entries (public keys and +%% attributes) to a binary. Possible attributes depends on the file +%% type. +%%-------------------------------------------------------------------- +ssh_encode(Entries, Type) when is_list(Entries), + Type == rfc4716_public_key; + Type == openssh_public_key; + Type == auth_keys; + Type == known_hosts -> + pubkey_ssh:encode(Entries, Type). + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -518,7 +545,6 @@ decrypt_public(CipherText, N,E, Options) -> crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)], Padding). - path_validation([], #path_validation_state{working_public_key_algorithm = Algorithm, working_public_key = diff --git a/lib/public_key/test/Makefile b/lib/public_key/test/Makefile index e20b903942..6889ae9a8a 100644 --- a/lib/public_key/test/Makefile +++ b/lib/public_key/test/Makefile @@ -80,7 +80,7 @@ release_tests_spec: opt $(INSTALL_DIR) $(RELSYSDIR) $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(COVER_FILE) $(HRL_FILES) $(RELSYSDIR) $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR) - chmod -f -R u+w $(RELSYSDIR) + chmod -R u+w $(RELSYSDIR) @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) release_docs_spec: diff --git a/lib/public_key/test/pkits_SUITE.erl b/lib/public_key/test/pkits_SUITE.erl index 660af4e8ab..a325a975e9 100644 --- a/lib/public_key/test/pkits_SUITE.erl +++ b/lib/public_key/test/pkits_SUITE.erl @@ -26,7 +26,6 @@ -compile(export_all). -include_lib("public_key/include/public_key.hrl"). -%%-include("public_key.hrl"). -define(error(Format,Args), error(Format,Args,?FILE,?LINE)). -define(warning(Format,Args), warning(Format,Args,?FILE,?LINE)). @@ -42,18 +41,65 @@ -define(NIST5, "2.16.840.1.101.3.2.1.48.5"). -define(NIST6, "2.16.840.1.101.3.2.1.48.6"). +-record(verify_state, { + certs_db, + crl_info, + revoke_state}). %% -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> - [signature_verification, validity_periods, - verifying_name_chaining, - verifying_paths_with_self_issued_certificates, - verifying_basic_constraints, key_usage, - name_constraints, private_certificate_extensions]. + [{group, signature_verification}, + {group, validity_periods}, + {group, verifying_name_chaining}, + {group, verifying_paths_with_self_issued_certificates}, + %%{group, basic_certificate_revocation_tests}, + %%{group, delta_crls}, + %%{group, distribution_points}, + {group, verifying_basic_constraints}, + {group, key_usage}, + {group, name_constraints}, + {group, private_certificate_extensions}]. groups() -> - []. + [{signature_verification, [], [valid_rsa_signature, + invalid_rsa_signature, valid_dsa_signature, + invalid_dsa_signature]}, + {validity_periods, [], + [not_before_invalid, not_before_valid, not_after_invalid, not_after_valid]}, + {verifying_name_chaining, [], + [invalid_name_chain, whitespace_name_chain, capitalization_name_chain, + uid_name_chain, attrib_name_chain, string_name_chain]}, + {verifying_paths_with_self_issued_certificates, [], + [basic_valid, basic_invalid, crl_signing_valid, crl_signing_invalid]}, + %% {basic_certificate_revocation_tests, [], + %% [missing_CRL, revoked_CA, revoked_peer, invalid_CRL_signature, + %% invalid_CRL_issuer, invalid_CRL, valid_CRL, + %% unknown_CRL_extension, old_CRL, fresh_CRL, valid_serial, + %% invalid_serial, valid_seperate_keys, invalid_separate_keys]}, + %% {delta_crls, [], [delta_without_crl, valid_delta_crls, invalid_delta_crls]}, + %% {distribution_points, [], [valid_distribution_points, + %% valid_distribution_points_no_issuing_distribution_point, + %% invalid_distribution_points, valid_only_contains, + %% invalid_only_contains, valid_only_some_reasons, + %% invalid_only_some_reasons, valid_indirect_crl, + %% invalid_indirect_crl, valid_crl_issuer, invalid_crl_issuer]}, + {verifying_basic_constraints,[], + [missing_basic_constraints, valid_basic_constraint, invalid_path_constraints, + valid_path_constraints]}, + {key_usage, [], + [invalid_key_usage, valid_key_usage]}, + {name_constraints, [], + [valid_DN_name_constraints, invalid_DN_name_constraints, + valid_rfc822_name_constraints, + invalid_rfc822_name_constraints, valid_DN_and_rfc822_name_constraints, + invalid_DN_and_rfc822_name_constraints, valid_dns_name_constraints, + invalid_dns_name_constraints, valid_uri_name_constraints, + invalid_uri_name_constraints]}, + {private_certificate_extensions, [], + [unknown_critical_extension, unknown_not_critical_extension]} + ]. init_per_group(_GroupName, Config) -> Config. @@ -61,112 +107,706 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(_Func, Config) -> + Datadir = proplists:get_value(data_dir, Config), + put(datadir, Datadir), + Config. + +end_per_testcase(_Func, Config) -> + Config. + +init_per_suite(Config) -> + {skip, "PKIX Conformance test certificates expired 14 of April 2011," + " new conformance test suite uses new format so skip until PKCS-12 support is implemented"}. + %% try crypto:start() of + %% ok -> + %% Config + %% catch _:_ -> + %% {skip, "Crypto did not start"} + %% end. + +end_per_suite(_Config) -> + application:stop(crypto). + +%%----------------------------------------------------------------------------- +valid_rsa_signature(doc) -> + ["Test rsa signatur verification"]; +valid_rsa_signature(suite) -> + []; +valid_rsa_signature(Config) when is_list(Config) -> + run([{ "4.1.1", "Valid Signatures Test1", ok}]). + +invalid_rsa_signature(doc) -> + ["Test rsa signatur verification"]; +invalid_rsa_signature(suite) -> + []; +invalid_rsa_signature(Config) when is_list(Config) -> + run([{ "4.1.2", "Invalid CA Signature Test2", {bad_cert,invalid_signature}}, + { "4.1.3", "Invalid EE Signature Test3", {bad_cert,invalid_signature}}]). + +valid_dsa_signature(doc) -> + ["Test dsa signatur verification"]; +valid_dsa_signature(suite) -> + []; +valid_dsa_signature(Config) when is_list(Config) -> + run([{ "4.1.4", "Valid DSA Signatures Test4", ok}, + { "4.1.5", "Valid DSA Parameter Inheritance Test5", ok}]). + +invalid_dsa_signature(doc) -> + ["Test dsa signatur verification"]; +invalid_dsa_signature(suite) -> + []; +invalid_dsa_signature(Config) when is_list(Config) -> + run([{ "4.1.6", "Invalid DSA Signature Test6",{bad_cert,invalid_signature}}]). +%%----------------------------------------------------------------------------- +not_before_invalid(doc) -> + [""]; +not_before_invalid(suite) -> + []; +not_before_invalid(Config) when is_list(Config) -> + run([{ "4.2.1", "Invalid CA notBefore Date Test1",{bad_cert, cert_expired}}, + { "4.2.2", "Invalid EE notBefore Date Test2",{bad_cert, cert_expired}}]). + +not_before_valid(doc) -> + [""]; +not_before_valid(suite) -> + []; +not_before_valid(Config) when is_list(Config) -> + run([{ "4.2.3", "Valid pre2000 UTC notBefore Date Test3", ok}, + { "4.2.4", "Valid GeneralizedTime notBefore Date Test4", ok}]). + +not_after_invalid(doc) -> + [""]; +not_after_invalid(suite) -> + []; +not_after_invalid(Config) when is_list(Config) -> + run([{ "4.2.5", "Invalid CA notAfter Date Test5", {bad_cert, cert_expired}}, + { "4.2.6", "Invalid EE notAfter Date Test6", {bad_cert, cert_expired}}, + { "4.2.7", "Invalid pre2000 UTC EE notAfter Date Test7",{bad_cert, cert_expired}}]). + +not_after_valid(doc) -> + [""]; +not_after_valid(suite) -> + []; +not_after_valid(Config) when is_list(Config) -> + run([{ "4.2.8", "Valid GeneralizedTime notAfter Date Test8", ok}]). +%%----------------------------------------------------------------------------- +invalid_name_chain(doc) -> + [""]; +invalid_name_chain(suite) -> + []; +invalid_name_chain(Config) when is_list(Config) -> + run([{ "4.3.1", "Invalid Name Chaining EE Test1", {bad_cert, invalid_issuer}}, + { "4.3.2", "Invalid Name Chaining Order Test2", {bad_cert, invalid_issuer}}]). + +whitespace_name_chain(doc) -> + [""]; +whitespace_name_chain(suite) -> + []; +whitespace_name_chain(Config) when is_list(Config) -> + run([{ "4.3.3", "Valid Name Chaining Whitespace Test3", ok}, + { "4.3.4", "Valid Name Chaining Whitespace Test4", ok}]). + +capitalization_name_chain(doc) -> + [""]; +capitalization_name_chain(suite) -> + []; +capitalization_name_chain(Config) when is_list(Config) -> + run([{ "4.3.5", "Valid Name Chaining Capitalization Test5",ok}]). + +uid_name_chain(doc) -> + [""]; +uid_name_chain(suite) -> + []; +uid_name_chain(Config) when is_list(Config) -> + run([{ "4.3.6", "Valid Name Chaining UIDs Test6",ok}]). + +attrib_name_chain(doc) -> + [""]; +attrib_name_chain(suite) -> + []; +attrib_name_chain(Config) when is_list(Config) -> + run([{ "4.3.7", "Valid RFC3280 Mandatory Attribute Types Test7", ok}, + { "4.3.8", "Valid RFC3280 Optional Attribute Types Test8", ok}]). + +string_name_chain(doc) -> + [""]; +string_name_chain(suite) -> + []; +string_name_chain(Config) when is_list(Config) -> + run([{ "4.3.9", "Valid UTF8String Encoded Names Test9", ok}, + { "4.3.10", "Valid Rollover from PrintableString to UTF8String Test10", ok}, + { "4.3.11", "Valid UTF8String Case Insensitive Match Test11", ok}]). + +%%----------------------------------------------------------------------------- + +basic_valid(doc) -> + [""]; +basic_valid(suite) -> + []; +basic_valid(Config) when is_list(Config) -> + run([{ "4.5.1", "Valid Basic Self-Issued Old With New Test1", ok}, + { "4.5.3", "Valid Basic Self-Issued New With Old Test3", ok}, + { "4.5.4", "Valid Basic Self-Issued New With Old Test4", ok} + ]). + +basic_invalid(doc) -> + [""]; +basic_invalid(suite) -> + []; +basic_invalid(Config) when is_list(Config) -> + run([{"4.5.2", "Invalid Basic Self-Issued Old With New Test2", + {bad_cert, {revoked, keyCompromise}}}, + {"4.5.5", "Invalid Basic Self-Issued New With Old Test5", + {bad_cert, {revoked, keyCompromise}}} + ]). + +crl_signing_valid(doc) -> + [""]; +crl_signing_valid(suite) -> + []; +crl_signing_valid(Config) when is_list(Config) -> + run([{ "4.5.6", "Valid Basic Self-Issued CRL Signing Key Test6", ok}]). + +crl_signing_invalid(doc) -> + [""]; +crl_signing_invalid(suite) -> + []; +crl_signing_invalid(Config) when is_list(Config) -> + run([{ "4.5.7", "Invalid Basic Self-Issued CRL Signing Key Test7", + {bad_cert, {revoked, keyCompromise}}}, + { "4.5.8", "Invalid Basic Self-Issued CRL Signing Key Test8", + {bad_cert, invalid_key_usage}} + ]). + +%%----------------------------------------------------------------------------- +missing_CRL(doc) -> + [""]; +missing_CRL(suite) -> + []; +missing_CRL(Config) when is_list(Config) -> + run([{ "4.4.1", "Missing CRL Test1",{bad_cert, + revocation_status_undetermined}}]). + +revoked_CA(doc) -> + [""]; +revoked_CA(suite) -> + []; +revoked_CA(Config) when is_list(Config) -> + run([{ "4.4.2", "Invalid Revoked CA Test2", {bad_cert, + {revoked, keyCompromise}}}]). + +revoked_peer(doc) -> + [""]; +revoked_peer(suite) -> + []; +revoked_peer(Config) when is_list(Config) -> + run([{ "4.4.3", "Invalid Revoked EE Test3", {bad_cert, + {revoked, keyCompromise}}}]). + +invalid_CRL_signature(doc) -> + [""]; +invalid_CRL_signature(suite) -> + []; +invalid_CRL_signature(Config) when is_list(Config) -> + run([{ "4.4.4", "Invalid Bad CRL Signature Test4", + {bad_cert, revocation_status_undetermined}}]). + +invalid_CRL_issuer(doc) -> + [""]; +invalid_CRL_issuer(suite) -> + []; +invalid_CRL_issuer(Config) when is_list(Config) -> + run({ "4.4.5", "Invalid Bad CRL Issuer Name Test5", + {bad_cert, revocation_status_undetermined}}). + +invalid_CRL(doc) -> + [""]; +invalid_CRL(suite) -> + []; +invalid_CRL(Config) when is_list(Config) -> + run([{ "4.4.6", "Invalid Wrong CRL Test6", + {bad_cert, revocation_status_undetermined}}]). + +valid_CRL(doc) -> + [""]; +valid_CRL(suite) -> + []; +valid_CRL(Config) when is_list(Config) -> + run([{ "4.4.7", "Valid Two CRLs Test7", ok}]). + +unknown_CRL_extension(doc) -> + [""]; +unknown_CRL_extension(suite) -> + []; +unknown_CRL_extension(Config) when is_list(Config) -> + run([{ "4.4.8", "Invalid Unknown CRL Entry Extension Test8", + {bad_cert, {revoked, keyCompromise}}}, + { "4.4.9", "Invalid Unknown CRL Extension Test9", + {bad_cert, {revoked, keyCompromise}}}, + { "4.4.10", "Invalid Unknown CRL Extension Test10", + {bad_cert, revocation_status_undetermined}}]). + +old_CRL(doc) -> + [""]; +old_CRL(suite) -> + []; +old_CRL(Config) when is_list(Config) -> + run([{ "4.4.11", "Invalid Old CRL nextUpdate Test11", + {bad_cert, revocation_status_undetermined}}, + { "4.4.12", "Invalid pre2000 CRL nextUpdate Test12", + {bad_cert, revocation_status_undetermined}}]). + +fresh_CRL(doc) -> + [""]; +fresh_CRL(suite) -> + []; +fresh_CRL(Config) when is_list(Config) -> + run([{ "4.4.13", "Valid GeneralizedTime CRL nextUpdate Test13", ok}]). + +valid_serial(doc) -> + [""]; +valid_serial(suite) -> + []; +valid_serial(Config) when is_list(Config) -> + run([ + { "4.4.14", "Valid Negative Serial Number Test14",ok}, + { "4.4.16", "Valid Long Serial Number Test16", ok}, + { "4.4.17", "Valid Long Serial Number Test17", ok} + ]). + +invalid_serial(doc) -> + [""]; +invalid_serial(suite) -> + []; +invalid_serial(Config) when is_list(Config) -> + run([{ "4.4.15", "Invalid Negative Serial Number Test15", + {bad_cert, {revoked, keyCompromise}}}, + { "4.4.18", "Invalid Long Serial Number Test18", + {bad_cert, {revoked, keyCompromise}}}]). + +valid_seperate_keys(doc) -> + [""]; +valid_seperate_keys(suite) -> + []; +valid_seperate_keys(Config) when is_list(Config) -> + run([{ "4.4.19", "Valid Separate Certificate and CRL Keys Test19", ok}]). + +invalid_separate_keys(doc) -> + [""]; +invalid_separate_keys(suite) -> + []; +invalid_separate_keys(Config) when is_list(Config) -> + run([{ "4.4.20", "Invalid Separate Certificate and CRL Keys Test20", + {bad_cert, {revoked, keyCompromise}}}, + { "4.4.21", "Invalid Separate Certificate and CRL Keys Test21", + {bad_cert, revocation_status_undetermined}} + ]). +%%----------------------------------------------------------------------------- +missing_basic_constraints(doc) -> + [""]; +missing_basic_constraints(suite) -> + []; +missing_basic_constraints(Config) when is_list(Config) -> + run([{ "4.6.1", "Invalid Missing basicConstraints Test1", + {bad_cert, missing_basic_constraint}}, + { "4.6.2", "Invalid cA False Test2", + {bad_cert, missing_basic_constraint}}, + { "4.6.3", "Invalid cA False Test3", + {bad_cert, missing_basic_constraint}}]). + +valid_basic_constraint(doc) -> + [""]; +valid_basic_constraint(suite) -> + []; +valid_basic_constraint(Config) when is_list(Config) -> + run([{"4.6.4", "Valid basicConstraints Not Critical Test4", ok}]). + +invalid_path_constraints(doc) -> + [""]; +invalid_path_constraints(suite) -> + []; +invalid_path_constraints(Config) when is_list(Config) -> + run([{ "4.6.5", "Invalid pathLenConstraint Test5", {bad_cert, max_path_length_reached}}, + { "4.6.6", "Invalid pathLenConstraint Test6", {bad_cert, max_path_length_reached}}, + { "4.6.9", "Invalid pathLenConstraint Test9", {bad_cert, max_path_length_reached}}, + { "4.6.10", "Invalid pathLenConstraint Test10", {bad_cert, max_path_length_reached}}, + { "4.6.11", "Invalid pathLenConstraint Test11", {bad_cert, max_path_length_reached}}, + { "4.6.12", "Invalid pathLenConstraint Test12", {bad_cert, max_path_length_reached}}, + { "4.6.16", "Invalid Self-Issued pathLenConstraint Test16", + {bad_cert, max_path_length_reached}}]). -signature_verification(doc) -> [""]; -signature_verification(suite) -> []; -signature_verification(Config) when is_list(Config) -> - run(signature_verification()). -validity_periods(doc) -> [""]; -validity_periods(suite) -> []; -validity_periods(Config) when is_list(Config) -> - run(validity_periods()). -verifying_name_chaining(doc) -> [""]; -verifying_name_chaining(suite) -> []; -verifying_name_chaining(Config) when is_list(Config) -> - run(verifying_name_chaining()). -basic_certificate_revocation_tests(doc) -> [""]; -basic_certificate_revocation_tests(suite) -> []; -basic_certificate_revocation_tests(Config) when is_list(Config) -> - run(basic_certificate_revocation_tests()). -verifying_paths_with_self_issued_certificates(doc) -> [""]; -verifying_paths_with_self_issued_certificates(suite) -> []; -verifying_paths_with_self_issued_certificates(Config) when is_list(Config) -> - run(verifying_paths_with_self_issued_certificates()). -verifying_basic_constraints(doc) -> [""]; -verifying_basic_constraints(suite) -> []; -verifying_basic_constraints(Config) when is_list(Config) -> - run(verifying_basic_constraints()). -key_usage(doc) -> [""]; -key_usage(suite) -> []; -key_usage(Config) when is_list(Config) -> - run(key_usage()). +valid_path_constraints(doc) -> + [""]; +valid_path_constraints(suite) -> + []; +valid_path_constraints(Config) when is_list(Config) -> + run([{ "4.6.7", "Valid pathLenConstraint Test7", ok}, + { "4.6.8", "Valid pathLenConstraint Test8", ok}, + { "4.6.13", "Valid pathLenConstraint Test13", ok}, + { "4.6.14", "Valid pathLenConstraint Test14", ok}, + { "4.6.15", "Valid Self-Issued pathLenConstraint Test15", ok}, + { "4.6.17", "Valid Self-Issued pathLenConstraint Test17", ok}]). + +%%----------------------------------------------------------------------------- +invalid_key_usage(doc) -> + [""]; +invalid_key_usage(suite) -> + []; +invalid_key_usage(Config) when is_list(Config) -> + run([{ "4.7.1", "Invalid keyUsage Critical keyCertSign False Test1", + {bad_cert,invalid_key_usage} }, + { "4.7.2", "Invalid keyUsage Not Critical keyCertSign False Test2", + {bad_cert,invalid_key_usage}}, + { "4.7.4", "Invalid keyUsage Critical cRLSign False Test4", + {bad_cert, revocation_status_undetermined}}, + { "4.7.5", "Invalid keyUsage Not Critical cRLSign False Test5", + {bad_cert, revocation_status_undetermined}} + ]). + +valid_key_usage(doc) -> + [""]; +valid_key_usage(suite) -> + []; +valid_key_usage(Config) when is_list(Config) -> + run([{ "4.7.3", "Valid keyUsage Not Critical Test3", ok}]). + +%%----------------------------------------------------------------------------- certificate_policies(doc) -> [""]; certificate_policies(suite) -> []; certificate_policies(Config) when is_list(Config) -> run(certificate_policies()). +%%----------------------------------------------------------------------------- require_explicit_policy(doc) -> [""]; require_explicit_policy(suite) -> []; require_explicit_policy(Config) when is_list(Config) -> run(require_explicit_policy()). +%%----------------------------------------------------------------------------- policy_mappings(doc) -> [""]; policy_mappings(suite) -> []; policy_mappings(Config) when is_list(Config) -> run(policy_mappings()). +%%----------------------------------------------------------------------------- inhibit_policy_mapping(doc) -> [""]; inhibit_policy_mapping(suite) -> []; inhibit_policy_mapping(Config) when is_list(Config) -> run(inhibit_policy_mapping()). +%%----------------------------------------------------------------------------- inhibit_any_policy(doc) -> [""]; inhibit_any_policy(suite) -> []; inhibit_any_policy(Config) when is_list(Config) -> run(inhibit_any_policy()). -name_constraints(doc) -> [""]; -name_constraints(suite) -> []; -name_constraints(Config) when is_list(Config) -> - run(name_constraints()). -distribution_points(doc) -> [""]; -distribution_points(suite) -> []; -distribution_points(Config) when is_list(Config) -> - run(distribution_points()). -delta_crls(doc) -> [""]; -delta_crls(suite) -> []; -delta_crls(Config) when is_list(Config) -> - run(delta_crls()). -private_certificate_extensions(doc) -> [""]; -private_certificate_extensions(suite) -> []; -private_certificate_extensions(Config) when is_list(Config) -> - run(private_certificate_extensions()). - -run() -> - Tests = - [signature_verification(), - validity_periods(), - verifying_name_chaining(), - %%basic_certificate_revocation_tests(), - verifying_paths_with_self_issued_certificates(), - verifying_basic_constraints(), - key_usage(), - %%certificate_policies(), - %%require_explicit_policy(), - %%policy_mappings(), - %%inhibit_policy_mapping(), - %%inhibit_any_policy(), - name_constraints(), - %distribution_points(), - %delta_crls(), - private_certificate_extensions() - ], - run(lists:append(Tests)). +%%----------------------------------------------------------------------------- + +valid_DN_name_constraints(doc) -> + [""]; +valid_DN_name_constraints(suite) -> + []; +valid_DN_name_constraints(Config) when is_list(Config) -> + run([{ "4.13.1", "Valid DN nameConstraints Test1", ok}, + { "4.13.4", "Valid DN nameConstraints Test4", ok}, + { "4.13.5", "Valid DN nameConstraints Test5", ok}, + { "4.13.6", "Valid DN nameConstraints Test6", ok}, + { "4.13.11", "Valid DN nameConstraints Test11", ok}, + { "4.13.14", "Valid DN nameConstraints Test14", ok}, + { "4.13.18", "Valid DN nameConstraints Test18", ok}, + { "4.13.19", "Valid Self-Issued DN nameConstraints Test19", ok}]). + +invalid_DN_name_constraints(doc) -> + [""]; +invalid_DN_name_constraints(suite) -> + []; +invalid_DN_name_constraints(Config) when is_list(Config) -> + run([{ "4.13.2", "Invalid DN nameConstraints Test2", {bad_cert, name_not_permitted}}, + { "4.13.3", "Invalid DN nameConstraints Test3", {bad_cert, name_not_permitted}}, + { "4.13.7", "Invalid DN nameConstraints Test7", {bad_cert, name_not_permitted}}, + { "4.13.8", "Invalid DN nameConstraints Test8", {bad_cert, name_not_permitted}}, + { "4.13.9", "Invalid DN nameConstraints Test9", {bad_cert, name_not_permitted}}, + { "4.13.10", "Invalid DN nameConstraints Test10",{bad_cert, name_not_permitted}}, + { "4.13.12", "Invalid DN nameConstraints Test12",{bad_cert, name_not_permitted}}, + { "4.13.13", "Invalid DN nameConstraints Test13",{bad_cert, name_not_permitted}}, + { "4.13.15", "Invalid DN nameConstraints Test15",{bad_cert, name_not_permitted}}, + { "4.13.16", "Invalid DN nameConstraints Test16",{bad_cert, name_not_permitted}}, + { "4.13.17", "Invalid DN nameConstraints Test17",{bad_cert, name_not_permitted}}, + { "4.13.20", "Invalid Self-Issued DN nameConstraints Test20", + {bad_cert, name_not_permitted}}]). + +valid_rfc822_name_constraints(doc) -> + [""]; +valid_rfc822_name_constraints(suite) -> + []; +valid_rfc822_name_constraints(Config) when is_list(Config) -> + run([{ "4.13.21", "Valid RFC822 nameConstraints Test21", ok}, + { "4.13.23", "Valid RFC822 nameConstraints Test23", ok}, + { "4.13.25", "Valid RFC822 nameConstraints Test25", ok}]). + + +invalid_rfc822_name_constraints(doc) -> + [""]; +invalid_rfc822_name_constraints(suite) -> + []; +invalid_rfc822_name_constraints(Config) when is_list(Config) -> + run([{ "4.13.22", "Invalid RFC822 nameConstraints Test22", + {bad_cert, name_not_permitted}}, + { "4.13.24", "Invalid RFC822 nameConstraints Test24", + {bad_cert, name_not_permitted}}, + { "4.13.26", "Invalid RFC822 nameConstraints Test26", + {bad_cert, name_not_permitted}}]). + +valid_DN_and_rfc822_name_constraints(doc) -> + [""]; +valid_DN_and_rfc822_name_constraints(suite) -> + []; +valid_DN_and_rfc822_name_constraints(Config) when is_list(Config) -> + run([{ "4.13.27", "Valid DN and RFC822 nameConstraints Test27", ok}]). + +invalid_DN_and_rfc822_name_constraints(doc) -> + [""]; +invalid_DN_and_rfc822_name_constraints(suite) -> + []; +invalid_DN_and_rfc822_name_constraints(Config) when is_list(Config) -> + run([{ "4.13.28", "Invalid DN and RFC822 nameConstraints Test28", + {bad_cert, name_not_permitted}}, + { "4.13.29", "Invalid DN and RFC822 nameConstraints Test29", + {bad_cert, name_not_permitted}}]). + +valid_dns_name_constraints(doc) -> + [""]; +valid_dns_name_constraints(suite) -> + []; +valid_dns_name_constraints(Config) when is_list(Config) -> + run([{ "4.13.30", "Valid DNS nameConstraints Test30", ok}, + { "4.13.32", "Valid DNS nameConstraints Test32", ok}]). + +invalid_dns_name_constraints(doc) -> + [""]; +invalid_dns_name_constraints(suite) -> + []; +invalid_dns_name_constraints(Config) when is_list(Config) -> + run([{ "4.13.31", "Invalid DNS nameConstraints Test31", {bad_cert, name_not_permitted}}, + { "4.13.33", "Invalid DNS nameConstraints Test33", {bad_cert, name_not_permitted}}, + { "4.13.38", "Invalid DNS nameConstraints Test38", {bad_cert, name_not_permitted}}]). + +valid_uri_name_constraints(doc) -> + [""]; +valid_uri_name_constraints(suite) -> + []; +valid_uri_name_constraints(Config) when is_list(Config) -> + run([{ "4.13.34", "Valid URI nameConstraints Test34", ok}, + { "4.13.36", "Valid URI nameConstraints Test36", ok}]). + +invalid_uri_name_constraints(doc) -> + [""]; +invalid_uri_name_constraints(suite) -> + []; +invalid_uri_name_constraints(Config) when is_list(Config) -> + run([{ "4.13.35", "Invalid URI nameConstraints Test35",{bad_cert, name_not_permitted}}, + { "4.13.37", "Invalid URI nameConstraints Test37",{bad_cert, name_not_permitted}}]). + +%%----------------------------------------------------------------------------- +delta_without_crl(doc) -> + [""]; +delta_without_crl(suite) -> + []; +delta_without_crl(Config) when is_list(Config) -> + run([{ "4.15.1", "Invalid deltaCRLIndicator No Base Test1",{bad_cert, + revocation_status_undetermined}}, + {"4.15.10", "Invalid delta-CRL Test10", {bad_cert, + revocation_status_undetermined}}]). + +valid_delta_crls(doc) -> + [""]; +valid_delta_crls(suite) -> + []; +valid_delta_crls(Config) when is_list(Config) -> + run([{ "4.15.2", "Valid delta-CRL Test2", ok}, + { "4.15.5", "Valid delta-CRL Test5", ok}, + { "4.15.7", "Valid delta-CRL Test7", ok}, + { "4.15.8", "Valid delta-CRL Test8", ok} + ]). + +invalid_delta_crls(doc) -> + [""]; +invalid_delta_crls(suite) -> + []; +invalid_delta_crls(Config) when is_list(Config) -> + run([{ "4.15.3", "Invalid delta-CRL Test3", {bad_cert,{revoked, keyCompromise}}}, + { "4.15.4", "Invalid delta-CRL Test4", {bad_cert,{revoked, keyCompromise}}}, + { "4.15.6", "Invalid delta-CRL Test6", {bad_cert,{revoked, keyCompromise}}}, + { "4.15.9", "Invalid delta-CRL Test9", {bad_cert,{revoked, keyCompromise}}}]). + +%%----------------------------------------------------------------------------- + +valid_distribution_points(doc) -> + [""]; +valid_distribution_points(suite) -> + []; +valid_distribution_points(Config) when is_list(Config) -> + run([{ "4.14.1", "Valid distributionPoint Test1", ok}, + { "4.14.4", "Valid distributionPoint Test4", ok}, + { "4.14.5", "Valid distributionPoint Test5", ok}, + { "4.14.7", "Valid distributionPoint Test7", ok} + ]). + +valid_distribution_points_no_issuing_distribution_point(doc) -> + [""]; +valid_distribution_points_no_issuing_distribution_point(suite) -> + []; +valid_distribution_points_no_issuing_distribution_point(Config) when is_list(Config) -> + run([{ "4.14.10", "Valid No issuingDistributionPoint Test10", ok} + ]). + +invalid_distribution_points(doc) -> + [""]; +invalid_distribution_points(suite) -> + []; +invalid_distribution_points(Config) when is_list(Config) -> + run([{ "4.14.2", "Invalid distributionPoint Test2", {bad_cert,{revoked, keyCompromise}}}, + { "4.14.3", "Invalid distributionPoint Test3", {bad_cert, + revocation_status_undetermined}}, + { "4.14.6", "Invalid distributionPoint Test6", {bad_cert,{revoked, keyCompromise}}}, + { "4.14.8", "Invalid distributionPoint Test8", {bad_cert, + revocation_status_undetermined}}, + { "4.14.9", "Invalid distributionPoint Test9", {bad_cert, + revocation_status_undetermined}} + ]). + +valid_only_contains(doc) -> + [""]; +valid_only_contains(suite) -> + []; +valid_only_contains(Config) when is_list(Config) -> + run([{ "4.14.13", "Valid onlyContainsCACerts CRL Test13", ok}]). + +invalid_only_contains(doc) -> + [""]; +invalid_only_contains(suite) -> + []; +invalid_only_contains(Config) when is_list(Config) -> + run([{ "4.14.11", "Invalid onlyContainsUserCerts CRL Test11", + {bad_cert, revocation_status_undetermined}}, + { "4.14.12", "Invalid onlyContainsCACerts CRL Test12", + {bad_cert, revocation_status_undetermined}}, + { "4.14.14", "Invalid onlyContainsAttributeCerts Test14", + {bad_cert, revocation_status_undetermined}} + ]). + +valid_only_some_reasons(doc) -> + [""]; +valid_only_some_reasons(suite) -> + []; +valid_only_some_reasons(Config) when is_list(Config) -> + run([{ "4.14.18", "Valid onlySomeReasons Test18", ok}, + { "4.14.19", "Valid onlySomeReasons Test19", ok} + ]). + +invalid_only_some_reasons(doc) -> + [""]; +invalid_only_some_reasons(suite) -> + []; +invalid_only_some_reasons(Config) when is_list(Config) -> + run([{ "4.14.15", "Invalid onlySomeReasons Test15", + {bad_cert,{revoked, keyCompromise}}}, + { "4.14.16", "Invalid onlySomeReasons Test16", + {bad_cert,{revoked, certificateHold}}}, + { "4.14.17", "Invalid onlySomeReasons Test17", + {bad_cert, revocation_status_undetermined}}, + { "4.14.20", "Invalid onlySomeReasons Test20", + {bad_cert,{revoked, keyCompromise}}}, + { "4.14.21", "Invalid onlySomeReasons Test21", + {bad_cert,{revoked, affiliationChanged}}} + ]). + +valid_indirect_crl(doc) -> + [""]; +valid_indirect_crl(suite) -> + []; +valid_indirect_crl(Config) when is_list(Config) -> + run([{ "4.14.22", "Valid IDP with indirectCRL Test22", ok}, + { "4.14.24", "Valid IDP with indirectCRL Test24", ok}, + { "4.14.25", "Valid IDP with indirectCRL Test25", ok} + ]). + +invalid_indirect_crl(doc) -> + [""]; +invalid_indirect_crl(suite) -> + []; +invalid_indirect_crl(Config) when is_list(Config) -> + run([{ "4.14.23", "Invalid IDP with indirectCRL Test23", + {bad_cert,{revoked, keyCompromise}}}, + { "4.14.26", "Invalid IDP with indirectCRL Test26", + {bad_cert, revocation_status_undetermined}} + ]). + +valid_crl_issuer(doc) -> + [""]; +valid_crl_issuer(suite) -> + []; +valid_crl_issuer(Config) when is_list(Config) -> + run([{ "4.14.28", "Valid cRLIssuer Test28", ok}%%, + %%{ "4.14.29", "Valid cRLIssuer Test29", ok}, + %%{ "4.14.33", "Valid cRLIssuer Test33", ok} + ]). + +invalid_crl_issuer(doc) -> + [""]; +invalid_crl_issuer(suite) -> + []; +invalid_crl_issuer(Config) when is_list(Config) -> + run([ + { "4.14.27", "Invalid cRLIssuer Test27", {bad_cert, revocation_status_undetermined}}, + { "4.14.31", "Invalid cRLIssuer Test31", {bad_cert,{revoked, keyCompromise}}}, + { "4.14.32", "Invalid cRLIssuer Test32", {bad_cert,{revoked, keyCompromise}}}, + { "4.14.34", "Invalid cRLIssuer Test34", {bad_cert,{revoked, keyCompromise}}}, + { "4.14.35", "Invalid cRLIssuer Test35", {bad_cert, revocation_status_undetermined}} + ]). + + +%%distribution_points() -> + %%{ "4.14", "Distribution Points" }, +%% [ + %% Although this test is valid it has a circular dependency. As a result + %% an attempt is made to reursively checks a CRL path and rejected due to + %% a CRL path validation error. PKITS notes suggest this test does not + %% need to be run due to this issue. +%% { "4.14.30", "Valid cRLIssuer Test30", 54 }]. + + +%%----------------------------------------------------------------------------- + +unknown_critical_extension(doc) -> + [""]; +unknown_critical_extension(suite) -> + []; +unknown_critical_extension(Config) when is_list(Config) -> + run([{ "4.16.2", "Invalid Unknown Critical Certificate Extension Test2", + {bad_cert,unknown_critical_extension}}]). + +unknown_not_critical_extension(doc) -> + [""]; +unknown_not_critical_extension(suite) -> + []; +unknown_not_critical_extension(Config) when is_list(Config) -> + run([{ "4.16.1", "Valid Unknown Not Critical Certificate Extension Test1", ok}]). + +%%----------------------------------------------------------------------------- run(Tests) -> File = file(?CERTS,"TrustAnchorRootCertificate.crt"), {ok, TA} = file:read_file(File), run(Tests, TA). run({Chap, Test, Result}, TA) -> - CertChain = sort_chain(read_certs(Test),TA, [], false), - try public_key:pkix_path_validation(TA, CertChain, []) of - {Result, _} -> ok; + CertChain = sort_chain(read_certs(Test),TA, [], false, Chap), + Options = path_validation_options(TA, Chap,Test), + try public_key:pkix_path_validation(TA, CertChain, Options) of + {Result, _} -> ok; {error,Result} when Result =/= ok -> ok; - {error,Error} when is_integer(Result) -> - ?warning(" ~p~n Got ~p expected ~p~n",[Test, Error, Result]); - {error,Error} when Result =/= ok -> - ?error(" minor ~p~n Got ~p expected ~p~n",[Test, Error, Result]); {error, Error} -> ?error(" ~p ~p~n Expected ~p got ~p ~n", [Chap, Test, Result, Error]), fail; - {ok, _} when Result =/= ok -> + {ok, _OK} when Result =/= ok -> ?error(" ~p ~p~n Expected ~p got ~p ~n", [Chap, Test, Result, ok]), fail catch Type:Reason -> @@ -181,14 +821,318 @@ run([Test|Rest],TA) -> run(Rest,TA); run([],_) -> ok. +path_validation_options(TA, Chap, Test) -> + case needs_crl_options(Chap) of + true -> + crl_options(TA, Test); + false -> + Fun = + fun(_,{bad_cert, _} = Reason, _) -> + {fail, Reason}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, Valid, UserState) when Valid == valid; + Valid == valid_peer -> + {valid, UserState} + end, + [{verify_fun, {Fun, []}}] + end. + +needs_crl_options("4.4" ++ _) -> + true; +needs_crl_options("4.5" ++ _) -> + true; +needs_crl_options("4.7.4" ++ _) -> + true; +needs_crl_options("4.7.5" ++ _) -> + true; +needs_crl_options("4.14" ++ _) -> + true; +needs_crl_options("4.15" ++ _) -> + true; +needs_crl_options(_) -> + false. + +crl_options(TA, Test) -> + case read_crls(Test) of + [] -> + []; + CRLs -> + Fun = + fun(_,{bad_cert, _} = Reason, _) -> + {fail, Reason}; + (_,{extension, + #'Extension'{extnID = ?'id-ce-cRLDistributionPoints', + extnValue = Value}}, UserState0) -> + UserState = update_crls(Value, UserState0), + {valid, UserState}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (OtpCert, Valid, UserState) when Valid == valid; + Valid == valid_peer -> + {ErlCerts, CRLs} = UserState#verify_state.crl_info, + CRLInfo0 = + crl_info(OtpCert, + ErlCerts,[{DerCRL, public_key:der_decode('CertificateList', + DerCRL)} || DerCRL <- CRLs], + []), + CRLInfo = lists:reverse(CRLInfo0), + Certs = UserState#verify_state.certs_db, + Fun = fun(DP, CRLtoValidate, Id, CertsDb) -> + trusted_cert_and_path(DP, CRLtoValidate, Id, CertsDb) + end, + Ignore = ignore_sign_test_when_building_path(Test), + case public_key:pkix_crls_validate(OtpCert, CRLInfo, + [{issuer_fun,{Fun, {Ignore, Certs}}}]) of + valid -> + {valid, UserState}; + Reason -> + {fail, Reason} + end + end, + + Certs = read_certs(Test), + ErlCerts = [public_key:pkix_decode_cert(Cert, otp) || Cert <- Certs], + + [{verify_fun, {Fun, #verify_state{certs_db = [TA| Certs], + crl_info = {ErlCerts, CRLs}}}}] + end. + +crl_info(_, _, [], Acc) -> + Acc; +crl_info(OtpCert, Certs, [{_, #'CertificateList'{tbsCertList = + #'TBSCertList'{issuer = Issuer, + crlExtensions = CRLExtensions}}} + = CRL | Rest], Acc) -> + OtpTBSCert = OtpCert#'OTPCertificate'.tbsCertificate, + Extensions = OtpTBSCert#'OTPTBSCertificate'.extensions, + ExtList = pubkey_cert:extensions_list(CRLExtensions), + DPs = case pubkey_cert:select_extension(?'id-ce-cRLDistributionPoints', Extensions) of + #'Extension'{extnValue = Value} -> + lists:map(fun(Point) -> pubkey_cert_records:transform(Point, decode) end, Value); + _ -> + case same_issuer(OtpCert, Issuer) of + true -> + [make_dp(ExtList, asn1_NOVALUE, Issuer)]; + false -> + [make_dp(ExtList, Issuer, ignore)] + end + end, + DPsCRLs = lists:map(fun(DP) -> {DP, CRL} end, DPs), + crl_info(OtpCert, Certs, Rest, DPsCRLs ++ Acc). + +ignore_sign_test_when_building_path("Invalid Bad CRL Signature Test4") -> + true; +ignore_sign_test_when_building_path(_) -> + false. + +same_issuer(OTPCert, Issuer) -> + DecIssuer = pubkey_cert_records:transform(Issuer, decode), + OTPTBSCert = OTPCert#'OTPCertificate'.tbsCertificate, + CertIssuer = OTPTBSCert#'OTPTBSCertificate'.issuer, + pubkey_cert:is_issuer(DecIssuer, CertIssuer). + +make_dp(Extensions, Issuer0, DpInfo) -> + {Issuer, Point} = mk_issuer_dp(Issuer0, DpInfo), + case pubkey_cert:select_extension('id-ce-cRLReason', Extensions) of + #'Extension'{extnValue = Reasons} -> + #'DistributionPoint'{cRLIssuer = Issuer, + reasons = Reasons, + distributionPoint = Point}; + _ -> + #'DistributionPoint'{cRLIssuer = Issuer, + reasons = [unspecified, keyCompromise, + cACompromise, affiliationChanged, superseded, + cessationOfOperation, certificateHold, + removeFromCRL, privilegeWithdrawn, aACompromise], + distributionPoint = Point} + end. + +mk_issuer_dp(asn1_NOVALUE, Issuer) -> + {asn1_NOVALUE, {fullName, [{directoryName, Issuer}]}}; +mk_issuer_dp(Issuer, _) -> + {[{directoryName, Issuer}], asn1_NOVALUE}. + +update_crls(_, State) -> + State. + +trusted_cert_and_path(DP, CRL, Id, {Ignore, CertsList}) -> + case crl_issuer(crl_issuer_name(DP), CRL, Id, CertsList, CertsList, Ignore) of + {ok, IssuerCert, DerIssuerCert} -> + Certs = [{public_key:pkix_decode_cert(Cert, otp), Cert} || Cert <- CertsList], + CertChain = build_chain(Certs, Certs, IssuerCert, Ignore, [DerIssuerCert]), + {ok, public_key:pkix_decode_cert(hd(CertChain), otp), CertChain}; + Other -> + Other + end. + +crl_issuer_name(#'DistributionPoint'{cRLIssuer = asn1_NOVALUE}) -> + undefined; +crl_issuer_name(#'DistributionPoint'{cRLIssuer = [{directoryName, Issuer}]}) -> + pubkey_cert_records:transform(Issuer, decode). + +build_chain([],_, _, _,Acc) -> + Acc; + +build_chain([{First, DerFirst}|Certs], All, Cert, Ignore, Acc) -> + case public_key:pkix_is_self_signed(Cert) andalso is_test_root(Cert) of + true -> + Acc; + false -> + case public_key:pkix_is_issuer(Cert, First) + %%andalso check_extension_cert_signer(First) + andalso is_signer(First, Cert, Ignore) + of + true -> + build_chain(All, All, First, Ignore, [DerFirst | Acc]); + false -> + build_chain(Certs, All, Cert, Ignore, Acc) + end + end. + +is_signer(_,_, true) -> + true; +is_signer(Signer, #'OTPCertificate'{} = Cert,_) -> + TBSCert = Signer#'OTPCertificate'.tbsCertificate, + PublicKeyInfo = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, + PublicKey = PublicKeyInfo#'OTPSubjectPublicKeyInfo'.subjectPublicKey, + AlgInfo = PublicKeyInfo#'OTPSubjectPublicKeyInfo'.algorithm, + PublicKeyParams = AlgInfo#'PublicKeyAlgorithm'.parameters, + try pubkey_cert:validate_signature(Cert, public_key:pkix_encode('OTPCertificate', + Cert, otp), + PublicKey, PublicKeyParams, true, ?DEFAULT_VERIFYFUN) of + true -> + true + catch + _:_ -> + false + end; +is_signer(Signer, #'CertificateList'{} = CRL, _) -> + TBSCert = Signer#'OTPCertificate'.tbsCertificate, + PublicKeyInfo = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, + PublicKey = PublicKeyInfo#'OTPSubjectPublicKeyInfo'.subjectPublicKey, + AlgInfo = PublicKeyInfo#'OTPSubjectPublicKeyInfo'.algorithm, + PublicKeyParams = AlgInfo#'PublicKeyAlgorithm'.parameters, + pubkey_crl:verify_crl_signature(CRL, public_key:pkix_encode('CertificateList', + CRL, plain), + PublicKey, PublicKeyParams). + +is_test_root(OtpCert) -> + TBSCert = OtpCert#'OTPCertificate'.tbsCertificate, + {rdnSequence, AtterList} = TBSCert#'OTPTBSCertificate'.issuer, + lists:member([{'AttributeTypeAndValue',{2,5,4,3},{printableString,"Trust Anchor"}}], + AtterList). + +check_extension_cert_signer(OtpCert) -> + TBSCert = OtpCert#'OTPCertificate'.tbsCertificate, + Extensions = TBSCert#'OTPTBSCertificate'.extensions, + case pubkey_cert:select_extension(?'id-ce-keyUsage', Extensions) of + #'Extension'{extnValue = KeyUse} -> + lists:member(keyCertSign, KeyUse); + _ -> + true + end. + +check_extension_crl_signer(OtpCert) -> + TBSCert = OtpCert#'OTPCertificate'.tbsCertificate, + Extensions = TBSCert#'OTPTBSCertificate'.extensions, + case pubkey_cert:select_extension(?'id-ce-keyUsage', Extensions) of + #'Extension'{extnValue = KeyUse} -> + lists:member(cRLSign, KeyUse); + _ -> + true + end. + +crl_issuer(undefined, CRL, issuer_not_found, _, CertsList, Ignore) -> + crl_issuer(CRL, CertsList, Ignore); + +crl_issuer(IssuerName, CRL, issuer_not_found, CertsList, CertsList, Ignore) -> + crl_issuer(IssuerName, CRL, IssuerName, CertsList, CertsList, Ignore); + +crl_issuer(undefined, CRL, Id, [Cert | Rest], All, false) -> + ErlCert = public_key:pkix_decode_cert(Cert, otp), + TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate, + SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber, + Issuer = public_key:pkix_normalize_name( + TBSCertificate#'OTPTBSCertificate'.subject), + Bool = is_signer(ErlCert, CRL, false), + case {SerialNumber, Issuer} of + Id when Bool == true -> + {ok, ErlCert, Cert}; + _ -> + crl_issuer(undefined, CRL, Id, Rest, All, false) + end; + +crl_issuer(IssuerName, CRL, Id, [Cert | Rest], All, false) -> + ErlCert = public_key:pkix_decode_cert(Cert, otp), + TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate, + SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber, + %%Issuer = public_key:pkix_normalize_name( + %% TBSCertificate#'OTPTBSCertificate'.subject), + Bool = is_signer(ErlCert, CRL, false), + case {SerialNumber, IssuerName} of + Id when Bool == true -> + {ok, ErlCert, Cert}; + {_, IssuerName} when Bool == true -> + {ok, ErlCert, Cert}; + _ -> + crl_issuer(IssuerName, CRL, Id, Rest, All, false) + end; + +crl_issuer(undefined, CRL, _, [], CertsList, Ignore) -> + crl_issuer(CRL, CertsList, Ignore); +crl_issuer(CRLName, CRL, _, [], CertsList, Ignore) -> + crl_issuer(CRLName, CRL, CertsList, Ignore). + + +crl_issuer(_, [],_) -> + {error, issuer_not_found}; +crl_issuer(CRL, [Cert | Rest], Ignore) -> + ErlCert = public_key:pkix_decode_cert(Cert, otp), + case public_key:pkix_is_issuer(CRL, ErlCert) andalso + check_extension_crl_signer(ErlCert) andalso + is_signer(ErlCert, CRL, Ignore) + of + true -> + {ok, ErlCert,Cert}; + false -> + crl_issuer(CRL, Rest, Ignore) + end. + +crl_issuer(_,_, [],_) -> + {error, issuer_not_found}; +crl_issuer(IssuerName, CRL, [Cert | Rest], Ignore) -> + ErlCert = public_key:pkix_decode_cert(Cert, otp), + TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate, + Issuer = public_key:pkix_normalize_name( + TBSCertificate#'OTPTBSCertificate'.subject), + + case + public_key:pkix_is_issuer(CRL, ErlCert) andalso + check_extension_crl_signer(ErlCert) andalso + is_signer(ErlCert, CRL, Ignore) + of + true -> + case pubkey_cert:is_issuer(Issuer, IssuerName) of + true -> + {ok, ErlCert,Cert}; + false -> + crl_issuer(IssuerName, CRL, Rest, Ignore) + end; + false -> + crl_issuer(IssuerName, CRL, Rest, Ignore) + end. read_certs(Test) -> File = test_file(Test), - %% io:format("Read ~p ",[File]), Ders = erl_make_certs:pem_to_der(File), - %% io:format("Ders ~p ~n",[length(Ders)]), [Cert || {'Certificate', Cert, not_encrypted} <- Ders]. +read_crls(Test) -> + File = test_file(Test), + Ders = erl_make_certs:pem_to_der(File), + [CRL || {'CertificateList', CRL, not_encrypted} <- Ders]. + test_file(Test) -> file(?CONV, lists:append(string:tokens(Test, " -")) ++ ".pem"). @@ -206,118 +1150,89 @@ file(Sub,File) -> end, AbsFile. -sort_chain([First|Certs], TA, Try, Found) -> +sort_chain(Certs, TA, Acc, Bool, Chap) when Chap == "4.5.3"-> + [CA, Entity, Self] = do_sort_chain(Certs, TA, Acc, Bool, Chap), + [CA, Self, Entity]; +sort_chain(Certs, TA, Acc, Bool, Chap) when Chap == "4.5.4"; + Chap == "4.5.5" -> + [CA, Entity, _Self] = do_sort_chain(Certs, TA, Acc, Bool, Chap), + [CA, Entity]; + +sort_chain(Certs, TA, Acc, Bool, Chap) when Chap == "4.14.24"; + Chap == "4.14.25"; + Chap == "4.14.26"; + Chap == "4.14.27"; + Chap == "4.14.31"; + Chap == "4.14.32"; + Chap == "4.14.33" -> + [_OtherCA, Entity, CA] = do_sort_chain(Certs, TA, Acc, Bool, Chap), + [CA, Entity]; + +sort_chain(Certs, TA, Acc, Bool, Chap) when Chap == "4.14.28"; + Chap == "4.14.29" -> + [CA, _OtherCA, Entity] = do_sort_chain(Certs, TA, Acc, Bool, Chap), + [CA, Entity]; + + +sort_chain(Certs, TA, Acc, Bool, Chap) when Chap == "4.14.33" -> + [Entity, CA, _OtherCA] = do_sort_chain(Certs, TA, Acc, Bool, Chap), + [CA, Entity]; + + +sort_chain(Certs, TA, Acc, Bool, Chap) -> + do_sort_chain(Certs, TA, Acc, Bool, Chap). + +do_sort_chain([First], TA, Try, Found, Chap) when Chap == "4.5.6"; + Chap == "4.5.7"; + Chap == "4.4.19"; + Chap == "4.4.20"; + Chap == "4.4.21"-> case public_key:pkix_is_issuer(First,TA) of true -> - [First|sort_chain(Certs,First,Try,true)]; + [First|do_sort_chain([],First,Try,true, Chap)]; false -> - sort_chain(Certs,TA,[First|Try],Found) + do_sort_chain([],TA,[First|Try],Found, Chap) end; -sort_chain([], _, [],_) -> []; -sort_chain([], Valid, Check, true) -> - sort_chain(lists:reverse(Check), Valid, [], false); -sort_chain([], _Valid, Check, false) -> +do_sort_chain([First|Certs], TA, Try, Found, Chap) when Chap == "4.5.6"; + Chap == "4.5.7"; + Chap == "4.4.19"; + Chap == "4.4.20"; + Chap == "4.4.21"-> +%% case check_extension_cert_signer(public_key:pkix_decode_cert(First, otp)) of +%% true -> + case public_key:pkix_is_issuer(First,TA) of + true -> + [First|do_sort_chain(Certs,First,Try,true, Chap)]; + false -> + do_sort_chain(Certs,TA,[First|Try],Found, Chap) + end; +%% false -> +%% do_sort_chain(Certs, TA, Try, Found, Chap) +%% end; + +do_sort_chain([First|Certs], TA, Try, Found, Chap) -> + case public_key:pkix_is_issuer(First,TA) of + true -> + [First|do_sort_chain(Certs,First,Try,true, Chap)]; + false -> + do_sort_chain(Certs,TA,[First|Try],Found, Chap) + end; + +do_sort_chain([], _, [],_, _) -> []; +do_sort_chain([], Valid, Check, true, Chap) -> + do_sort_chain(lists:reverse(Check), Valid, [], false, Chap); +do_sort_chain([], _Valid, Check, false, _) -> Check. -signature_verification() -> - %% "4.1", "Signature Verification" , - [{ "4.1.1", "Valid Signatures Test1", ok}, - { "4.1.2", "Invalid CA Signature Test2", {bad_cert,invalid_signature}}, - { "4.1.3", "Invalid EE Signature Test3", {bad_cert,invalid_signature}}, - { "4.1.4", "Valid DSA Signatures Test4", ok}, - { "4.1.5", "Valid DSA Parameter Inheritance Test5", ok}, - { "4.1.6", "Invalid DSA Signature Test6", {bad_cert,invalid_signature}}]. -validity_periods() -> - %% { "4.2", "Validity Periods" }, - [{ "4.2.1", "Invalid CA notBefore Date Test1", {bad_cert, cert_expired}}, - { "4.2.2", "Invalid EE notBefore Date Test2", {bad_cert, cert_expired}}, - { "4.2.3", "Valid pre2000 UTC notBefore Date Test3", ok}, - { "4.2.4", "Valid GeneralizedTime notBefore Date Test4", ok}, - { "4.2.5", "Invalid CA notAfter Date Test5", {bad_cert, cert_expired}}, - { "4.2.6", "Invalid EE notAfter Date Test6", {bad_cert, cert_expired}}, - { "4.2.7", "Invalid pre2000 UTC EE notAfter Date Test7", {bad_cert, cert_expired}}, - { "4.2.8", "Valid GeneralizedTime notAfter Date Test8", ok}]. -verifying_name_chaining() -> - %%{ "4.3", "Verifying Name Chaining" }, - [{ "4.3.1", "Invalid Name Chaining EE Test1", {bad_cert, invalid_issuer}}, - { "4.3.2", "Invalid Name Chaining Order Test2", {bad_cert, invalid_issuer}}, - { "4.3.3", "Valid Name Chaining Whitespace Test3", ok}, - { "4.3.4", "Valid Name Chaining Whitespace Test4", ok}, - { "4.3.5", "Valid Name Chaining Capitalization Test5", ok}, - { "4.3.6", "Valid Name Chaining UIDs Test6", ok}, - { "4.3.7", "Valid RFC3280 Mandatory Attribute Types Test7", ok}, - { "4.3.8", "Valid RFC3280 Optional Attribute Types Test8", ok}, - { "4.3.9", "Valid UTF8String Encoded Names Test9", ok}, - { "4.3.10", "Valid Rollover from PrintableString to UTF8String Test10", ok}, - { "4.3.11", "Valid UTF8String Case Insensitive Match Test11", ok}]. -basic_certificate_revocation_tests() -> - %%{ "4.4", "Basic Certificate Revocation Tests" }, - [{ "4.4.1", "Missing CRL Test1", 3 }, - { "4.4.2", "Invalid Revoked CA Test2", 23 }, - { "4.4.3", "Invalid Revoked EE Test3", 23 }, - { "4.4.4", "Invalid Bad CRL Signature Test4", 8 }, - { "4.4.5", "Invalid Bad CRL Issuer Name Test5", 3 }, - { "4.4.6", "Invalid Wrong CRL Test6", 3 }, - { "4.4.7", "Valid Two CRLs Test7", ok}, - - %% The test document suggests these should return certificate revoked... - %% Subsquent discussion has concluded they should not due to unhandle - %% critical CRL extensions. - { "4.4.8", "Invalid Unknown CRL Entry Extension Test8", 36 }, - { "4.4.9", "Invalid Unknown CRL Extension Test9", 36 }, - - { "4.4.10", "Invalid Unknown CRL Extension Test10", 36 }, - { "4.4.11", "Invalid Old CRL nextUpdate Test11", 12 }, - { "4.4.12", "Invalid pre2000 CRL nextUpdate Test12", 12 }, - { "4.4.13", "Valid GeneralizedTime CRL nextUpdate Test13", ok}, - { "4.4.14", "Valid Negative Serial Number Test14", ok}, - { "4.4.15", "Invalid Negative Serial Number Test15", 23 }, - { "4.4.16", "Valid Long Serial Number Test16", ok}, - { "4.4.17", "Valid Long Serial Number Test17", ok}, - { "4.4.18", "Invalid Long Serial Number Test18", 23 }, - { "4.4.19", "Valid Separate Certificate and CRL Keys Test19", ok}, - { "4.4.20", "Invalid Separate Certificate and CRL Keys Test20", 23 }, - - %% CRL path is revoked so get a CRL path validation error - { "4.4.21", "Invalid Separate Certificate and CRL Keys Test21", 54 }]. -verifying_paths_with_self_issued_certificates() -> - %%{ "4.5", "Verifying Paths with Self-Issued Certificates" }, - [{ "4.5.1", "Valid Basic Self-Issued Old With New Test1", ok}, - %%{ "4.5.2", "Invalid Basic Self-Issued Old With New Test2", 23 }, - %%{ "4.5.3", "Valid Basic Self-Issued New With Old Test3", ok}, - %%{ "4.5.4", "Valid Basic Self-Issued New With Old Test4", ok}, - { "4.5.5", "Invalid Basic Self-Issued New With Old Test5", 23 }, - %%{ "4.5.6", "Valid Basic Self-Issued CRL Signing Key Test6", ok}, - { "4.5.7", "Invalid Basic Self-Issued CRL Signing Key Test7", 23 }, - { "4.5.8", "Invalid Basic Self-Issued CRL Signing Key Test8", {bad_cert,invalid_key_usage} }]. -verifying_basic_constraints() -> - [%%{ "4.6", "Verifying Basic Constraints" }, - { "4.6.1", "Invalid Missing basicConstraints Test1", - {bad_cert, missing_basic_constraint} }, - { "4.6.2", "Invalid cA False Test2", {bad_cert, missing_basic_constraint}}, - { "4.6.3", "Invalid cA False Test3", {bad_cert, missing_basic_constraint}}, - { "4.6.4", "Valid basicConstraints Not Critical Test4", ok}, - { "4.6.5", "Invalid pathLenConstraint Test5", {bad_cert, max_path_length_reached}}, - { "4.6.6", "Invalid pathLenConstraint Test6", {bad_cert, max_path_length_reached}}, - { "4.6.7", "Valid pathLenConstraint Test7", ok}, - { "4.6.8", "Valid pathLenConstraint Test8", ok}, - { "4.6.9", "Invalid pathLenConstraint Test9", {bad_cert, max_path_length_reached}}, - { "4.6.10", "Invalid pathLenConstraint Test10", {bad_cert, max_path_length_reached}}, - { "4.6.11", "Invalid pathLenConstraint Test11", {bad_cert, max_path_length_reached}}, - { "4.6.12", "Invalid pathLenConstraint Test12", {bad_cert, max_path_length_reached}}, - { "4.6.13", "Valid pathLenConstraint Test13", ok}, - { "4.6.14", "Valid pathLenConstraint Test14", ok}, - { "4.6.15", "Valid Self-Issued pathLenConstraint Test15", ok}, - { "4.6.16", "Invalid Self-Issued pathLenConstraint Test16", {bad_cert, max_path_length_reached}}, - { "4.6.17", "Valid Self-Issued pathLenConstraint Test17", ok}]. -key_usage() -> - %%{ "4.7", "Key Usage" }, - [{ "4.7.1", "Invalid keyUsage Critical keyCertSign False Test1", {bad_cert,invalid_key_usage} }, - { "4.7.2", "Invalid keyUsage Not Critical keyCertSign False Test2", {bad_cert,invalid_key_usage} }, - { "4.7.3", "Valid keyUsage Not Critical Test3", ok} - %%,{ "4.7.4", "Invalid keyUsage Critical cRLSign False Test4", 35 } - %%,{ "4.7.5", "Invalid keyUsage Not Critical cRLSign False Test5", 35 } - ]. +error(Format, Args, File0, Line) -> + File = filename:basename(File0), + Pid = group_leader(), + Pid ! {failed, File, Line}, + io:format(Pid, "~s(~p): ERROR"++Format, [File,Line|Args]). + +warning(Format, Args, File0, Line) -> + File = filename:basename(File0), + io:format("~s(~p): Warning "++Format, [File,Line|Args]). %% Certificate policy tests need special handling. They can have several %% sub tests and we need to check the outputs are correct. @@ -425,182 +1340,3 @@ inhibit_any_policy() -> {"4.12.8", "Invalid Self-Issued inhibitAnyPolicy Test8", 43 }, {"4.12.9", "Valid Self-Issued inhibitAnyPolicy Test9", ok}, {"4.12.10", "Invalid Self-Issued inhibitAnyPolicy Test10", 43 }]. - -name_constraints() -> - %%{ "4.13", "Name Constraints" }, - [{ "4.13.1", "Valid DN nameConstraints Test1", ok}, - { "4.13.2", "Invalid DN nameConstraints Test2", {bad_cert, name_not_permitted}}, - { "4.13.3", "Invalid DN nameConstraints Test3", {bad_cert, name_not_permitted}}, - { "4.13.4", "Valid DN nameConstraints Test4", ok}, - { "4.13.5", "Valid DN nameConstraints Test5", ok}, - { "4.13.6", "Valid DN nameConstraints Test6", ok}, - { "4.13.7", "Invalid DN nameConstraints Test7", {bad_cert, name_not_permitted}}, - { "4.13.8", "Invalid DN nameConstraints Test8", {bad_cert, name_not_permitted}}, - { "4.13.9", "Invalid DN nameConstraints Test9", {bad_cert, name_not_permitted}}, - { "4.13.10", "Invalid DN nameConstraints Test10", {bad_cert, name_not_permitted}}, - { "4.13.11", "Valid DN nameConstraints Test11", ok}, - { "4.13.12", "Invalid DN nameConstraints Test12", {bad_cert, name_not_permitted}}, - { "4.13.13", "Invalid DN nameConstraints Test13", {bad_cert, name_not_permitted}}, - { "4.13.14", "Valid DN nameConstraints Test14", ok}, - { "4.13.15", "Invalid DN nameConstraints Test15", {bad_cert, name_not_permitted}}, - { "4.13.16", "Invalid DN nameConstraints Test16", {bad_cert, name_not_permitted}}, - { "4.13.17", "Invalid DN nameConstraints Test17", {bad_cert, name_not_permitted}}, - { "4.13.18", "Valid DN nameConstraints Test18", ok}, - { "4.13.19", "Valid Self-Issued DN nameConstraints Test19", ok}, - { "4.13.20", "Invalid Self-Issued DN nameConstraints Test20", {bad_cert, name_not_permitted} }, - { "4.13.21", "Valid RFC822 nameConstraints Test21", ok}, - { "4.13.22", "Invalid RFC822 nameConstraints Test22", {bad_cert, name_not_permitted} }, - { "4.13.23", "Valid RFC822 nameConstraints Test23", ok}, - { "4.13.24", "Invalid RFC822 nameConstraints Test24", {bad_cert, name_not_permitted} }, - { "4.13.25", "Valid RFC822 nameConstraints Test25", ok}, - { "4.13.26", "Invalid RFC822 nameConstraints Test26", {bad_cert, name_not_permitted}}, - { "4.13.27", "Valid DN and RFC822 nameConstraints Test27", ok}, - { "4.13.28", "Invalid DN and RFC822 nameConstraints Test28", {bad_cert, name_not_permitted} }, - { "4.13.29", "Invalid DN and RFC822 nameConstraints Test29", {bad_cert, name_not_permitted} }, - { "4.13.30", "Valid DNS nameConstraints Test30", ok}, - { "4.13.31", "Invalid DNS nameConstraints Test31", {bad_cert, name_not_permitted} }, - { "4.13.32", "Valid DNS nameConstraints Test32", ok}, - { "4.13.33", "Invalid DNS nameConstraints Test33", {bad_cert, name_not_permitted}}, - { "4.13.34", "Valid URI nameConstraints Test34", ok}, - { "4.13.35", "Invalid URI nameConstraints Test35", {bad_cert, name_not_permitted} }, - { "4.13.36", "Valid URI nameConstraints Test36", ok}, - { "4.13.37", "Invalid URI nameConstraints Test37", {bad_cert, name_not_permitted}}, - { "4.13.38", "Invalid DNS nameConstraints Test38", {bad_cert, name_not_permitted} }]. -distribution_points() -> - %%{ "4.14", "Distribution Points" }, - [{ "4.14.1", "Valid distributionPoint Test1", ok}, - { "4.14.2", "Invalid distributionPoint Test2", 23 }, - { "4.14.3", "Invalid distributionPoint Test3", 44 }, - { "4.14.4", "Valid distributionPoint Test4", ok}, - { "4.14.5", "Valid distributionPoint Test5", ok}, - { "4.14.6", "Invalid distributionPoint Test6", 23 }, - { "4.14.7", "Valid distributionPoint Test7", ok}, - { "4.14.8", "Invalid distributionPoint Test8", 44 }, - { "4.14.9", "Invalid distributionPoint Test9", 44 }, - { "4.14.10", "Valid No issuingDistributionPoint Test10", ok}, - { "4.14.11", "Invalid onlyContainsUserCerts CRL Test11", 44 }, - { "4.14.12", "Invalid onlyContainsCACerts CRL Test12", 44 }, - { "4.14.13", "Valid onlyContainsCACerts CRL Test13", ok}, - { "4.14.14", "Invalid onlyContainsAttributeCerts Test14", 44 }, - { "4.14.15", "Invalid onlySomeReasons Test15", 23 }, - { "4.14.16", "Invalid onlySomeReasons Test16", 23 }, - { "4.14.17", "Invalid onlySomeReasons Test17", 3 }, - { "4.14.18", "Valid onlySomeReasons Test18", ok}, - { "4.14.19", "Valid onlySomeReasons Test19", ok}, - { "4.14.20", "Invalid onlySomeReasons Test20", 23 }, - { "4.14.21", "Invalid onlySomeReasons Test21", 23 }, - { "4.14.22", "Valid IDP with indirectCRL Test22", ok}, - { "4.14.23", "Invalid IDP with indirectCRL Test23", 23 }, - { "4.14.24", "Valid IDP with indirectCRL Test24", ok}, - { "4.14.25", "Valid IDP with indirectCRL Test25", ok}, - { "4.14.26", "Invalid IDP with indirectCRL Test26", 44 }, - { "4.14.27", "Invalid cRLIssuer Test27", 3 }, - { "4.14.28", "Valid cRLIssuer Test28", ok}, - { "4.14.29", "Valid cRLIssuer Test29", ok}, - - %% Although this test is valid it has a circular dependency. As a result - %% an attempt is made to reursively checks a CRL path and rejected due to - %% a CRL path validation error. PKITS notes suggest this test does not - %% need to be run due to this issue. - { "4.14.30", "Valid cRLIssuer Test30", 54 }, - { "4.14.31", "Invalid cRLIssuer Test31", 23 }, - { "4.14.32", "Invalid cRLIssuer Test32", 23 }, - { "4.14.33", "Valid cRLIssuer Test33", ok}, - { "4.14.34", "Invalid cRLIssuer Test34", 23 }, - { "4.14.35", "Invalid cRLIssuer Test35", 44 }]. -delta_crls() -> - %%{ "4.15", "Delta-CRLs" }, - [{ "4.15.1", "Invalid deltaCRLIndicator No Base Test1", 3 }, - { "4.15.2", "Valid delta-CRL Test2", ok}, - { "4.15.3", "Invalid delta-CRL Test3", 23 }, - { "4.15.4", "Invalid delta-CRL Test4", 23 }, - { "4.15.5", "Valid delta-CRL Test5", ok}, - { "4.15.6", "Invalid delta-CRL Test6", 23 }, - { "4.15.7", "Valid delta-CRL Test7", ok}, - { "4.15.8", "Valid delta-CRL Test8", ok}, - { "4.15.9", "Invalid delta-CRL Test9", 23 }, - { "4.15.10", "Invalid delta-CRL Test10", 12 }]. -private_certificate_extensions() -> - %%{ "4.16", "Private Certificate Extensions" }, - [{ "4.16.1", "Valid Unknown Not Critical Certificate Extension Test1", ok}, - { "4.16.2", "Invalid Unknown Critical Certificate Extension Test2", - {bad_cert,unknown_critical_extension}}]. - - -convert() -> - Tests = [signature_verification(), - validity_periods(), - verifying_name_chaining(), - basic_certificate_revocation_tests(), - verifying_paths_with_self_issued_certificates(), - verifying_basic_constraints(), - key_usage(), - certificate_policies(), - require_explicit_policy(), - policy_mappings(), - inhibit_policy_mapping(), - inhibit_any_policy(), - name_constraints(), - distribution_points(), - delta_crls(), - private_certificate_extensions()], - [convert(Test) || Test <- lists:flatten(Tests)]. - -convert({_,Test,_}) -> - convert1(Test); -convert({_,Test,_,_,_,_,_}) -> - convert1(Test). - -convert1(Test) -> - FName = lists:append(string:tokens(Test, " -")), - File = filename:join(?MIME, "Signed" ++ FName ++ ".eml"), - io:format("Convert ~p~n",[File]), - {ok, Mail} = file:read_file(File), - Base64 = skip_lines(Mail), - %%io:format("~s",[Base64]), - Tmp = base64:mime_decode(Base64), - file:write_file("pkits/smime-pem/tmp-pkcs7.der", Tmp), - Cmd = "openssl pkcs7 -inform der -in pkits/smime-pem/tmp-pkcs7.der" - " -print_certs -out pkits/smime-pem/" ++ FName ++ ".pem", - case os:cmd(Cmd) of - "" -> ok; - Err -> - io:format("~s",[Err]), - erlang:error(bad_cmd) - end. - -skip_lines(<<"\r\n\r\n", Rest/binary>>) -> Rest; -skip_lines(<<"\n\n", Rest/binary>>) -> Rest; -skip_lines(<<_:8, Rest/binary>>) -> - skip_lines(Rest). - -init_per_testcase(_Func, Config) -> - Datadir = proplists:get_value(data_dir, Config), - put(datadir, Datadir), - Config. - -end_per_testcase(_Func, Config) -> - %% Nodes = select_nodes(all, Config, ?FILE, ?LINE), - %% rpc:multicall(Nodes, mnesia, lkill, []), - Config. - -init_per_suite(Config) -> - try crypto:start() of - ok -> - Config - catch _:_ -> - {skip, "Crypto did not start"} - end. - -end_per_suite(_Config) -> - application:stop(crypto). - -error(Format, Args, File0, Line) -> - File = filename:basename(File0), - Pid = group_leader(), - Pid ! {failed, File, Line}, - io:format(Pid, "~s(~p): ERROR"++Format, [File,Line|Args]). - -warning(Format, Args, File0, Line) -> - File = filename:basename(File0), - io:format("~s(~p): Warning "++Format, [File,Line|Args]). diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 6c482f9c30..b11e4d092a 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -102,11 +102,23 @@ end_per_testcase(_TestCase, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [app, pk_decode_encode, encrypt_decrypt, sign_verify, + [app, + {group, pem_decode_encode}, + {group, ssh_public_key_decode_encode}, + encrypt_decrypt, + {group, sign_verify}, pkix, pkix_path_validation, deprecated]. groups() -> - []. + [{pem_decode_encode, [], [dsa_pem, rsa_pem, encrypted_pem, + dh_pem, cert_pem]}, + {ssh_public_key_decode_encode, [], + [ssh_rsa_public_key, ssh_dsa_public_key, ssh_rfc4716_rsa_comment, + ssh_rfc4716_dsa_comment, ssh_rfc4716_rsa_subject, ssh_known_hosts, + ssh_auth_keys, ssh1_known_hosts, ssh1_auth_keys, ssh_openssh_public_key_with_comment, + ssh_openssh_public_key_long_header]}, + {sign_verify, [], [rsa_sign_verify, dsa_sign_verify]} + ]. init_per_group(_GroupName, Config) -> Config. @@ -125,22 +137,20 @@ app(suite) -> app(Config) when is_list(Config) -> ok = test_server:app_test(public_key). -pk_decode_encode(doc) -> - ["Tests pem_decode/1, pem_encode/1, " - "der_decode/2, der_encode/2, " - "pem_entry_decode/1, pem_entry_decode/2," - "pem_entry_encode/2, pem_entry_encode/3."]; +%%-------------------------------------------------------------------- -pk_decode_encode(suite) -> +dsa_pem(doc) -> + [""]; +dsa_pem(suite) -> []; -pk_decode_encode(Config) when is_list(Config) -> +dsa_pem(Config) when is_list(Config) -> Datadir = ?config(data_dir, Config), - [{'DSAPrivateKey', DerDSAKey, not_encrypted} = Entry0 ] = - erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")), - + [{'DSAPrivateKey', DerDSAKey, not_encrypted} = Entry0 ] = + erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")), + DSAKey = public_key:der_decode('DSAPrivateKey', DerDSAKey), - + DSAKey = public_key:pem_entry_decode(Entry0), {ok, DSAPubPem} = file:read_file(filename:join(Datadir, "dsa_pub.pem")), @@ -150,74 +160,107 @@ pk_decode_encode(Config) when is_list(Config) -> true = check_entry_type(DSAPubKey, 'DSAPublicKey'), PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', DSAPubKey), DSAPubPemNoEndNewLines = strip_ending_newlines(DSAPubPem), - DSAPubPemEndNoNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])), - - [{'RSAPrivateKey', DerRSAKey, not_encrypted} = Entry1 ] = + DSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])). + +%%-------------------------------------------------------------------- + +rsa_pem(doc) -> + [""]; +rsa_pem(suite) -> + []; +rsa_pem(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + [{'RSAPrivateKey', DerRSAKey, not_encrypted} = Entry0 ] = erl_make_certs:pem_to_der(filename:join(Datadir, "client_key.pem")), - + RSAKey0 = public_key:der_decode('RSAPrivateKey', DerRSAKey), + + RSAKey0 = public_key:pem_entry_decode(Entry0), - RSAKey0 = public_key:pem_entry_decode(Entry1), - - [{'RSAPrivateKey', _, {_,_}} = Entry2] = + [{'RSAPrivateKey', _, {_,_}} = Entry1] = erl_make_certs:pem_to_der(filename:join(Datadir, "rsa.pem")), - - true = check_entry_type(public_key:pem_entry_decode(Entry2, "abcd1234"), + + true = check_entry_type(public_key:pem_entry_decode(Entry1, "abcd1234"), 'RSAPrivateKey'), {ok, RSAPubPem} = file:read_file(filename:join(Datadir, "rsa_pub.pem")), - [{'SubjectPublicKeyInfo', _, _} = PubEntry1] = + [{'SubjectPublicKeyInfo', _, _} = PubEntry0] = public_key:pem_decode(RSAPubPem), - RSAPubKey = public_key:pem_entry_decode(PubEntry1), + RSAPubKey = public_key:pem_entry_decode(PubEntry0), true = check_entry_type(RSAPubKey, 'RSAPublicKey'), - PubEntry1 = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey), + PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey), RSAPubPemNoEndNewLines = strip_ending_newlines(RSAPubPem), - RSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry1])), + RSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])), {ok, RSARawPem} = file:read_file(filename:join(Datadir, "rsa_pub_key.pem")), - [{'RSAPublicKey', _, _} = PubEntry2] = + [{'RSAPublicKey', _, _} = PubEntry1] = public_key:pem_decode(RSARawPem), - RSAPubKey = public_key:pem_entry_decode(PubEntry2), + RSAPubKey = public_key:pem_entry_decode(PubEntry1), RSARawPemNoEndNewLines = strip_ending_newlines(RSARawPem), - RSARawPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry2])), + RSARawPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry1])). + +%%-------------------------------------------------------------------- + +encrypted_pem(doc) -> + [""]; +encrypted_pem(suite) -> + []; +encrypted_pem(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + [{'RSAPrivateKey', DerRSAKey, not_encrypted}] = + erl_make_certs:pem_to_der(filename:join(Datadir, "client_key.pem")), + + RSAKey = public_key:der_decode('RSAPrivateKey', DerRSAKey), Salt0 = crypto:rand_bytes(8), - Entry3 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey0, + Entry0 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey, {{"DES-EDE3-CBC", Salt0}, "1234abcd"}), - - RSAKey0 = public_key:pem_entry_decode(Entry3,"1234abcd"), - + RSAKey = public_key:pem_entry_decode(Entry0,"1234abcd"), Des3KeyFile = filename:join(Datadir, "des3_client_key.pem"), + erl_make_certs:der_to_pem(Des3KeyFile, [Entry0]), + [{'RSAPrivateKey', _, {"DES-EDE3-CBC", Salt0}}] = + erl_make_certs:pem_to_der(Des3KeyFile), - erl_make_certs:der_to_pem(Des3KeyFile, [Entry3]), - - [{'RSAPrivateKey', _, {"DES-EDE3-CBC", Salt0}}] = erl_make_certs:pem_to_der(Des3KeyFile), - Salt1 = crypto:rand_bytes(8), - Entry4 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey0, + Entry1 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey, {{"DES-CBC", Salt1}, "4567efgh"}), - - DesKeyFile = filename:join(Datadir, "des_client_key.pem"), + erl_make_certs:der_to_pem(DesKeyFile, [Entry1]), + [{'RSAPrivateKey', _, {"DES-CBC", Salt1}} =Entry2] = + erl_make_certs:pem_to_der(DesKeyFile), + true = check_entry_type(public_key:pem_entry_decode(Entry2, "4567efgh"), + 'RSAPrivateKey'). - erl_make_certs:der_to_pem(DesKeyFile, [Entry4]), - - [{'RSAPrivateKey', _, {"DES-CBC", Salt1}} =Entry5] = erl_make_certs:pem_to_der(DesKeyFile), - - - true = check_entry_type(public_key:pem_entry_decode(Entry5, "4567efgh"), - 'RSAPrivateKey'), +%%-------------------------------------------------------------------- - [{'DHParameter', DerDH, not_encrypted} = Entry6] = +dh_pem(doc) -> + [""]; +dh_pem(suite) -> + []; +dh_pem(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + [{'DHParameter', DerDH, not_encrypted} = Entry] = erl_make_certs:pem_to_der(filename:join(Datadir, "dh.pem")), - - erl_make_certs:der_to_pem(filename:join(Datadir, "new_dh.pem"), [Entry6]), + + erl_make_certs:der_to_pem(filename:join(Datadir, "new_dh.pem"), [Entry]), DHParameter = public_key:der_decode('DHParameter', DerDH), - DHParameter = public_key:pem_entry_decode(Entry6), + DHParameter = public_key:pem_entry_decode(Entry), - Entry6 = public_key:pem_entry_encode('DHParameter', DHParameter), + Entry = public_key:pem_entry_encode('DHParameter', DHParameter). +%%-------------------------------------------------------------------- +cert_pem(doc) -> + [""]; +cert_pem(suite) -> + []; +cert_pem(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + [Entry0] = + erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")), + [{'Certificate', DerCert, not_encrypted} = Entry7] = erl_make_certs:pem_to_der(filename:join(Datadir, "client_cert.pem")), @@ -227,15 +270,232 @@ pk_decode_encode(Config) when is_list(Config) -> CertEntries = [{'Certificate', _, not_encrypted} = CertEntry0, {'Certificate', _, not_encrypted} = CertEntry1] = erl_make_certs:pem_to_der(filename:join(Datadir, "cacerts.pem")), - + ok = erl_make_certs:der_to_pem(filename:join(Datadir, "wcacerts.pem"), CertEntries), ok = erl_make_certs:der_to_pem(filename:join(Datadir, "wdsa.pem"), [Entry0]), NewCertEntries = erl_make_certs:pem_to_der(filename:join(Datadir, "wcacerts.pem")), true = lists:member(CertEntry0, NewCertEntries), true = lists:member(CertEntry1, NewCertEntries), - [Entry0] = erl_make_certs:pem_to_der(filename:join(Datadir, "wdsa.pem")), - ok. + [Entry0] = erl_make_certs:pem_to_der(filename:join(Datadir, "wdsa.pem")). + +%%-------------------------------------------------------------------- +ssh_rsa_public_key(doc) -> + ""; +ssh_rsa_public_key(suite) -> + []; +ssh_rsa_public_key(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_pub")), + [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, public_key), + [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, rfc4716_public_key), + + {ok, RSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_rsa_pub")), + [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, public_key), + [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, openssh_public_key), + + %% Can not check EncodedSSh == RSARawSsh2 and EncodedOpenSsh + %% = RSARawOpenSsh as line breakpoints may differ + + EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key), + EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key), + + [{PubKey, Attributes1}] = + public_key:ssh_decode(EncodedSSh, public_key), + [{PubKey, Attributes2}] = + public_key:ssh_decode(EncodedOpenSsh, public_key). + +%%-------------------------------------------------------------------- + +ssh_dsa_public_key(doc) -> + ""; +ssh_dsa_public_key(suite) -> + []; +ssh_dsa_public_key(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_pub")), + [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, public_key), + [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, rfc4716_public_key), + + {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_pub")), + [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, public_key), + [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key), + + %% Can not check EncodedSSh == DSARawSsh2 and EncodedOpenSsh + %% = DSARawOpenSsh as line breakpoints may differ + + EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key), + EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key), + + [{PubKey, Attributes1}] = + public_key:ssh_decode(EncodedSSh, public_key), + [{PubKey, Attributes2}] = + public_key:ssh_decode(EncodedOpenSsh, public_key). + +%%-------------------------------------------------------------------- +ssh_rfc4716_rsa_comment(doc) -> + "Test comment header and rsa key"; +ssh_rfc4716_rsa_comment(suite) -> + []; +ssh_rfc4716_rsa_comment(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_comment_pub")), + [{#'RSAPublicKey'{} = PubKey, Attributes}] = + public_key:ssh_decode(RSARawSsh2, public_key), + + Headers = proplists:get_value(headers, Attributes), + + Value = proplists:get_value("Comment", Headers, undefined), + true = Value =/= undefined, + RSARawSsh2 = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key). + +%%-------------------------------------------------------------------- +ssh_rfc4716_dsa_comment(doc) -> + "Test comment header and dsa key"; +ssh_rfc4716_dsa_comment(suite) -> + []; +ssh_rfc4716_dsa_comment(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_comment_pub")), + [{{_, #'Dss-Parms'{}} = PubKey, Attributes}] = + public_key:ssh_decode(DSARawSsh2, public_key), + + Headers = proplists:get_value(headers, Attributes), + + Value = proplists:get_value("Comment", Headers, undefined), + true = Value =/= undefined, + + %% Can not check Encoded == DSARawSsh2 as line continuation breakpoints may differ + Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key), + [{PubKey, Attributes}] = + public_key:ssh_decode(Encoded, public_key). + +%%-------------------------------------------------------------------- +ssh_rfc4716_rsa_subject(doc) -> + "Test another header value than comment"; +ssh_rfc4716_rsa_subject(suite) -> + []; +ssh_rfc4716_rsa_subject(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_subject_pub")), + [{#'RSAPublicKey'{} = PubKey, Attributes}] = + public_key:ssh_decode(RSARawSsh2, public_key), + + Headers = proplists:get_value(headers, Attributes), + + Value = proplists:get_value("Subject", Headers, undefined), + true = Value =/= undefined, + + %% Can not check Encoded == RSARawSsh2 as line continuation breakpoints may differ + Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key), + [{PubKey, Attributes}] = + public_key:ssh_decode(Encoded, public_key). + +%%-------------------------------------------------------------------- +ssh_known_hosts(doc) -> + ""; +ssh_known_hosts(suite) -> + []; +ssh_known_hosts(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "known_hosts")), + [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded = + public_key:ssh_decode(SshKnownHosts, known_hosts), + + Value1 = proplists:get_value(hostnames, Attributes1, undefined), + Value2 = proplists:get_value(hostnames, Attributes2, undefined), + true = (Value1 =/= undefined) and (Value2 =/= undefined), + + Encoded = public_key:ssh_encode(Decoded, known_hosts), + Decoded = public_key:ssh_decode(Encoded, known_hosts). + +%%-------------------------------------------------------------------- + +ssh1_known_hosts(doc) -> + ""; +ssh1_known_hosts(suite) -> + []; +ssh1_known_hosts(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "ssh1_known_hosts")), + [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded = + public_key:ssh_decode(SshKnownHosts, known_hosts), + + Value1 = proplists:get_value(hostnames, Attributes1, undefined), + Value2 = proplists:get_value(hostnames, Attributes2, undefined), + true = (Value1 =/= undefined) and (Value2 =/= undefined), + + Encoded = public_key:ssh_encode(Decoded, known_hosts), + Decoded = public_key:ssh_decode(Encoded, known_hosts). + +%%-------------------------------------------------------------------- +ssh_auth_keys(doc) -> + ""; +ssh_auth_keys(suite) -> + []; +ssh_auth_keys(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "auth_keys")), + [{#'RSAPublicKey'{}, Attributes1}, {{_, #'Dss-Parms'{}}, _Attributes2}] = Decoded = + public_key:ssh_decode(SshAuthKeys, auth_keys), + + Value1 = proplists:get_value(options, Attributes1, undefined), + true = Value1 =/= undefined, + + Encoded = public_key:ssh_encode(Decoded, auth_keys), + Decoded = public_key:ssh_decode(Encoded, auth_keys). + +%%-------------------------------------------------------------------- +ssh1_auth_keys(doc) -> + ""; +ssh1_auth_keys(suite) -> + []; +ssh1_auth_keys(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "ssh1_auth_keys")), + [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded = + public_key:ssh_decode(SshAuthKeys, auth_keys), + + Value1 = proplists:get_value(bits, Attributes1, undefined), + Value2 = proplists:get_value(bits, Attributes2, undefined), + true = (Value1 =/= undefined) and (Value2 =/= undefined), + + Encoded = public_key:ssh_encode(Decoded, auth_keys), + Decoded = public_key:ssh_decode(Encoded, auth_keys). + +%%-------------------------------------------------------------------- +ssh_openssh_public_key_with_comment(doc) -> + "Test that emty lines and lines starting with # are ignored"; +ssh_openssh_public_key_with_comment(suite) -> + []; +ssh_openssh_public_key_with_comment(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_with_comment_pub")), + [{{_, #'Dss-Parms'{}}, _}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key). + +%%-------------------------------------------------------------------- +ssh_openssh_public_key_long_header(doc) -> + "Test that long headers are handled"; +ssh_openssh_public_key_long_header(suite) -> + []; +ssh_openssh_public_key_long_header(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok,RSARawOpenSsh} = file:read_file(filename:join(Datadir, "ssh_rsa_long_header_pub")), + [{#'RSAPublicKey'{}, _}] = Decoded = public_key:ssh_decode(RSARawOpenSsh, public_key), + + Encoded = public_key:ssh_encode(Decoded, rfc4716_public_key), + Decoded = public_key:ssh_decode(Encoded, rfc4716_public_key). %%-------------------------------------------------------------------- encrypt_decrypt(doc) -> @@ -258,44 +518,49 @@ encrypt_decrypt(Config) when is_list(Config) -> ok. %%-------------------------------------------------------------------- -sign_verify(doc) -> - ["Checks that we can sign and verify signatures."]; -sign_verify(suite) -> +rsa_sign_verify(doc) -> + ["Checks that we can sign and verify rsa signatures."]; +rsa_sign_verify(suite) -> []; -sign_verify(Config) when is_list(Config) -> - %% Make cert signs and validates the signature using RSA and DSA +rsa_sign_verify(Config) when is_list(Config) -> Ca = {_, CaKey} = erl_make_certs:make_cert([]), + {Cert1, _} = erl_make_certs:make_cert([{key, dsa}, {issuer, Ca}]), PrivateRSA = #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} = public_key:pem_entry_decode(CaKey), - - CertInfo = {Cert1,CertKey1} = erl_make_certs:make_cert([{key, dsa}, {issuer, Ca}]), - PublicRSA = #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}, true = public_key:pkix_verify(Cert1, PublicRSA), - {Cert2,_CertKey} = erl_make_certs:make_cert([{issuer, CertInfo}]), - - #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y, x=_X} = - public_key:pem_entry_decode(CertKey1), - true = public_key:pkix_verify(Cert2, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}), - - %% RSA sign Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")), - RSASign = public_key:sign(Msg, sha, PrivateRSA), true = public_key:verify(Msg, sha, RSASign, PublicRSA), false = public_key:verify(<<1:8, Msg/binary>>, sha, RSASign, PublicRSA), false = public_key:verify(Msg, sha, <<1:8, RSASign/binary>>, PublicRSA), RSASign1 = public_key:sign(Msg, md5, PrivateRSA), - true = public_key:verify(Msg, md5, RSASign1, PublicRSA), + true = public_key:verify(Msg, md5, RSASign1, PublicRSA). - %% DSA sign +%%-------------------------------------------------------------------- + +dsa_sign_verify(doc) -> + ["Checks that we can sign and verify dsa signatures."]; +dsa_sign_verify(suite) -> + []; +dsa_sign_verify(Config) when is_list(Config) -> + Ca = erl_make_certs:make_cert([]), + CertInfo = {_,CertKey1} = erl_make_certs:make_cert([{key, dsa}, {issuer, Ca}]), + {Cert2,_CertKey} = erl_make_certs:make_cert([{issuer, CertInfo}]), + + #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y, x=_X} = + public_key:pem_entry_decode(CertKey1), + true = public_key:pkix_verify(Cert2, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}), + Datadir = ?config(data_dir, Config), [DsaKey = {'DSAPrivateKey', _, _}] = erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")), DSAPrivateKey = public_key:pem_entry_decode(DsaKey), #'DSAPrivateKey'{p=P1, q=Q1, g=G1, y=Y1, x=_X1} = DSAPrivateKey, + + Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")), DSASign = public_key:sign(Msg, sha, DSAPrivateKey), DSAPublicKey = Y1, DSAParams = #'Dss-Parms'{p=P1, q=Q1, g=G1}, @@ -312,9 +577,8 @@ sign_verify(Config) when is_list(Config) -> false = public_key:verify(<<1:8, RestDigest/binary>>, none, DigestSign, {DSAPublicKey, DSAParams}), false = public_key:verify(Digest, none, <<1:8, DigestSign/binary>>, - {DSAPublicKey, DSAParams}), - - ok. + {DSAPublicKey, DSAParams}). + %%-------------------------------------------------------------------- pkix(doc) -> "Misc pkix tests not covered elsewhere"; diff --git a/lib/public_key/test/public_key_SUITE_data/auth_keys b/lib/public_key/test/public_key_SUITE_data/auth_keys new file mode 100644 index 0000000000..0c4b47edde --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/auth_keys @@ -0,0 +1,3 @@ +command="dump /home",no-pty,no-port-forwarding ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmtisaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRuLDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368+dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNWjeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4fuKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV225JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRMIB+X+OTUUI8= dhopson@VMUbuntu-DSH + +ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH diff --git a/lib/public_key/test/public_key_SUITE_data/known_hosts b/lib/public_key/test/public_key_SUITE_data/known_hosts new file mode 100644 index 0000000000..30fc3b1fe8 --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/known_hosts @@ -0,0 +1,3 @@ +hostname.domain.com,192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= + +|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= [email protected] diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub new file mode 100644 index 0000000000..a765ba8189 --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub new file mode 100644 index 0000000000..d5a34a3f78 --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub @@ -0,0 +1,3 @@ +#This should be ignored!! + +ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub b/lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub new file mode 100644 index 0000000000..0a0838db40 --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmtisaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRuLDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368+dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNWjeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4fuKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV225JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRMIB+X+OTUUI8= dhopson@VMUbuntu-DSH diff --git a/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys b/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys new file mode 100644 index 0000000000..c91f4e4679 --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys @@ -0,0 +1,3 @@ +1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH + +command="dump /home",no-pty,no-port-forwarding 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH diff --git a/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts b/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts new file mode 100644 index 0000000000..ec668fe05b --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts @@ -0,0 +1,2 @@ +hostname.domain.com,192.168.0.1 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH +hostname2.domain.com,192.168.0.2 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub new file mode 100644 index 0000000000..ca5089dbd7 --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub @@ -0,0 +1,13 @@ +---- BEGIN SSH2 PUBLIC KEY ---- +Comment: This is my public key for use on \ +servers which I don't like. +AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbET +W6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdH +YI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5c +vwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGf +J0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAA +vioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACB +AN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HS +n24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5 +sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV +---- END SSH2 PUBLIC KEY ---- diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub new file mode 100644 index 0000000000..a5e38be81a --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub @@ -0,0 +1,12 @@ +---- BEGIN SSH2 PUBLIC KEY ---- +Comment: DSA Public Key for use with MyIsp +AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbET +W6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdH +YI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5c +vwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGf +J0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAA +vioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACB +AN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HS +n24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5 +sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV +---- END SSH2 PUBLIC KEY ---- diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub new file mode 100644 index 0000000000..e4d446147c --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub @@ -0,0 +1,7 @@ +---- BEGIN SSH2 PUBLIC KEY ---- +Comment: "1024-bit RSA, converted from OpenSSH by [email protected]" +x-command: /home/me/bin/lock-in-guest.sh +AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb +YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ +5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE= +---- END SSH2 PUBLIC KEY ---- diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub new file mode 100644 index 0000000000..761088b517 --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub @@ -0,0 +1,13 @@ +---- BEGIN SSH2 PUBLIC KEY ---- +AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o +39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS +7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmt +isaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2 +sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRu +LDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368 ++dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNW +jeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4f +uKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV22 +5JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRM +IB+X+OTUUI8= +---- END SSH2 PUBLIC KEY ---- diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub new file mode 100644 index 0000000000..8b8ccda8ba --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub @@ -0,0 +1,8 @@ +---- BEGIN SSH2 PUBLIC KEY ---- +Subject: me +Comment: 1024-bit rsa, created by [email protected] Mon Jan 15 \ +08:31:24 2001 +AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 +596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 +soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0= +---- END SSH2 PUBLIC KEY ---- diff --git a/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub new file mode 100644 index 0000000000..7b42ced93e --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub @@ -0,0 +1,9 @@ +---- BEGIN SSH2 PUBLIC KEY ---- +Comment: This is an example of a very very very very looooooooooooo\ +ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong\ +commment +x-command: /home/me/bin/lock-in-guest.sh +AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb +YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ +5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE= +---- END SSH2 PUBLIC KEY ---- diff --git a/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub new file mode 100644 index 0000000000..7b42ced93e --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub @@ -0,0 +1,9 @@ +---- BEGIN SSH2 PUBLIC KEY ---- +Comment: This is an example of a very very very very looooooooooooo\ +ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong\ +commment +x-command: /home/me/bin/lock-in-guest.sh +AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb +YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ +5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE= +---- END SSH2 PUBLIC KEY ---- diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk index c99fd6fee1..3c6b012152 100644 --- a/lib/public_key/vsn.mk +++ b/lib/public_key/vsn.mk @@ -1 +1 @@ -PUBLIC_KEY_VSN = 0.11 +PUBLIC_KEY_VSN = 0.12 |