aboutsummaryrefslogtreecommitdiffstats
path: root/lib/public_key
diff options
context:
space:
mode:
Diffstat (limited to 'lib/public_key')
-rw-r--r--lib/public_key/asn1/OTP-PKIX.asn113
-rw-r--r--lib/public_key/asn1/README2
-rw-r--r--lib/public_key/doc/src/Makefile3
-rw-r--r--lib/public_key/doc/src/book.xml2
-rw-r--r--lib/public_key/doc/src/cert_records.xml39
-rw-r--r--lib/public_key/doc/src/introduction.xml2
-rw-r--r--lib/public_key/doc/src/notes.xml190
-rw-r--r--lib/public_key/doc/src/part.xml5
-rw-r--r--lib/public_key/doc/src/part_notes.xml2
-rw-r--r--lib/public_key/doc/src/public_key.xml581
-rw-r--r--lib/public_key/doc/src/public_key_records.xml2
-rw-r--r--lib/public_key/doc/src/ref_man.xml2
-rw-r--r--lib/public_key/doc/src/using_public_key.xml504
-rw-r--r--lib/public_key/include/public_key.hrl29
-rw-r--r--lib/public_key/src/Makefile6
-rw-r--r--lib/public_key/src/pubkey_cert.erl590
-rw-r--r--lib/public_key/src/pubkey_cert_records.erl484
-rw-r--r--lib/public_key/src/pubkey_crypto.erl160
-rw-r--r--lib/public_key/src/pubkey_pem.erl255
-rw-r--r--lib/public_key/src/pubkey_ssh.erl431
-rw-r--r--lib/public_key/src/public_key.app.src10
-rw-r--r--lib/public_key/src/public_key.appup.src52
-rw-r--r--lib/public_key/src/public_key.erl831
-rw-r--r--lib/public_key/test/Makefile8
-rw-r--r--lib/public_key/test/erl_make_certs.erl421
-rw-r--r--lib/public_key/test/pkits_SUITE.erl1502
-rw-r--r--lib/public_key/test/public_key.cover4
-rw-r--r--lib/public_key/test/public_key.spec3
-rw-r--r--lib/public_key/test/public_key_SUITE.erl736
-rw-r--r--lib/public_key/test/public_key_SUITE_data/auth_keys3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/dsa_pub.pem12
-rw-r--r--lib/public_key/test/public_key_SUITE_data/known_hosts3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub1
-rw-r--r--lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub1
-rw-r--r--lib/public_key/test/public_key_SUITE_data/rsa_pub.pem4
-rw-r--r--lib/public_key/test/public_key_SUITE_data/rsa_pub_key.pem4
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts2
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub13
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub12
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub7
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub13
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub8
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub9
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub9
-rw-r--r--lib/public_key/vsn.mk9
47 files changed, 4970 insertions, 2015 deletions
diff --git a/lib/public_key/asn1/OTP-PKIX.asn1 b/lib/public_key/asn1/OTP-PKIX.asn1
index 2bcacc0990..ad704191a9 100644
--- a/lib/public_key/asn1/OTP-PKIX.asn1
+++ b/lib/public_key/asn1/OTP-PKIX.asn1
@@ -302,18 +302,25 @@ SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= {
-- DSA Keys and Signatures
+
+ DSAParams ::= CHOICE
+ {
+ params Dss-Parms,
+ null NULL
+ }
+
-- SubjectPublicKeyInfo:
dsa PUBLIC-KEY-ALGORITHM-CLASS ::= {
ID id-dsa
- TYPE Dss-Parms -- XXX Must be OPTIONAL
+ TYPE DSAParams -- XXX Must be OPTIONAL
PUBLIC-KEY-TYPE DSAPublicKey }
-- Certificate.signatureAlgorithm
dsa-with-sha1 SIGNATURE-ALGORITHM-CLASS ::= {
- ID id-dsa-with-sha1
- TYPE NULL } -- XXX Must be empty and not NULL
+ ID id-dsa-with-sha1
+ TYPE DSAParams }
--
-- RSA Keys and Signatures
diff --git a/lib/public_key/asn1/README b/lib/public_key/asn1/README
index 5fb8cf9725..2a880e2d51 100644
--- a/lib/public_key/asn1/README
+++ b/lib/public_key/asn1/README
@@ -46,6 +46,6 @@ diff -r1.1 PKIXAttributeCertificate.asn1
---
> version AttCertVersion, -- version is v2
-4. Defenitions of publuic keys from PKCS-1.asn1 present in
+4. Definitions of public keys from PKCS-1.asn1 present in
PKIX1Algorithms88.asn1 where removed as we take them directly from
PKCS-1.asn1 \ No newline at end of file
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/book.xml b/lib/public_key/doc/src/book.xml
index d3b8c7a2c7..f8d1205e57 100644
--- a/lib/public_key/doc/src/book.xml
+++ b/lib/public_key/doc/src/book.xml
@@ -5,7 +5,7 @@
<header titlestyle="normal">
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/public_key/doc/src/cert_records.xml b/lib/public_key/doc/src/cert_records.xml
index 8cfe57f670..ad4f5812cb 100644
--- a/lib/public_key/doc/src/cert_records.xml
+++ b/lib/public_key/doc/src/cert_records.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -37,7 +37,7 @@
<p>This chapter briefly describes erlang records derived from asn1
specifications used to handle X509 certificates. The intent is to
describe the data types and not to specify the meaning of each
- component for this we refer you to RFC 3280.
+ component for this we refer you to RFC 5280.
</p>
<p>Use the following include directive to get access to the
@@ -45,11 +45,7 @@
<code> -include_lib("public_key/include/public_key.hrl"). </code>
- <p>The used specification is available in <c>OTP-PKIX.asn1</c>,
- which is an amelioration of
- the <c>PKIX1Explicit88.asn1</c>, <c>PKIX1Implicit88.asn1</c>
- and <c>PKIX1Algorithms88.asn1</c> modules.
- You find all these modules in the <c>asn1</c> subdirectory
+ <p>The used asn1 specifications are available <c>asn1</c> subdirectory
of the application <c>public_key</c>.
</p>
@@ -62,6 +58,9 @@
marker="public_key">public key reference manual </seealso> or
follows here.</p>
+ <p><c>oid() - a tuple of integers
+ as generated by the asn1 compiler.</c></p>
+
<p><c>time() = uct_time() | general_time()</c></p>
<p><c>uct_time() = {utcTime, "YYMMDDHHMMSSZ"} </c></p>
@@ -119,9 +118,31 @@
algorithm, % oid()
parameters % asn1_der_encoded()
}.
+</code>
+
+<code>
+#'OTPCertificate'{
+ tbsCertificate, % #'OTPTBSCertificate'{}
+ signatureAlgorithm, % #'SignatureAlgorithm'
+ signature % {0, binary()} - asn1 compact bitstring
+ }.
+
+#'OTPTBSCertificate'{
+ version, % v1 | v2 | v3
+ serialNumber, % integer()
+ signature, % #'SignatureAlgorithm'
+ issuer, % {rdnSequence, [#AttributeTypeAndValue'{}]}
+ validity, % #'Validity'{}
+ subject, % {rdnSequence, [#AttributeTypeAndValue'{}]}
+ subjectPublicKeyInfo, % #'SubjectPublicKeyInfo'{}
+ issuerUniqueID, % binary() | asn1_novalue
+ subjectUniqueID, % binary() | asn1_novalue
+ extensions % [#'Extension'{}]
+ }.
+
#'SignatureAlgorithm'{
algorithm, % id_signature_algorithm()
- parameters % public_key_params()
+ parameters % asn1_novalue | #'Dss-Parms'{}
}.
</code>
diff --git a/lib/public_key/doc/src/introduction.xml b/lib/public_key/doc/src/introduction.xml
index 71488e435a..8cf11ee10e 100644
--- a/lib/public_key/doc/src/introduction.xml
+++ b/lib/public_key/doc/src/introduction.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml
index 33a424f432..efd4a37eb9 100644
--- a/lib/public_key/doc/src/notes.xml
+++ b/lib/public_key/doc/src/notes.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -33,6 +33,192 @@
<rev>A</rev>
<file>notes.xml</file>
</header>
+
+<section><title>Public_Key 0.13</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ replace "a ssl" with "an ssl" reindent
+ pkix_path_validation/3 Trivial documentation fixes
+ (Thanks to Christian von Roques )</p>
+ <p>
+ Own Id: OTP-9464</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<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>
+ <list>
+ <item>
+ <p>
+ Allows the public_key module to decode and encode RSA and
+ DSA keys encoded using the SubjectPublicKeyInfo format.
+ When pem_entry_encode is called on an RSA or DSA public
+ key type, the key is wrapped in the SubjectPublicKeyInfo
+ format.</p>
+ <p>
+ Own Id: OTP-9061</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Public_Key 0.10</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved dialyzer specs.</p>
+ <p>
+ Own Id: OTP-8964</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Public_Key 0.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Updated ssl to ignore CA certs that violate the asn1-spec
+ for a certificate, and updated public key asn1 spec to
+ handle inherited DSS-params.</p>
+ <p>
+ Own Id: OTP-7884</p>
+ </item>
+ <item>
+ <p>
+ Changed ssl implementation to retain backwards
+ compatibility for old option {verify, 0} that shall be
+ equivalent to {verify, verify_none}, also separate the
+ cases unknown ca and selfsigned peer cert, and restored
+ return value of deprecated function
+ public_key:pem_to_der/1.</p>
+ <p>
+ Own Id: OTP-8858</p>
+ </item>
+ <item>
+ <p>
+ Better handling of v1 and v2 certificates. V1 and v2
+ certificates does not have any extensions so then
+ validate_extensions should just accept that there are
+ none and not end up in missing_basic_constraints clause.</p>
+ <p>
+ Own Id: OTP-8867</p>
+ </item>
+ <item>
+ <p>
+ Changed the verify fun so that it differentiate between
+ the peer certificate and CA certificates by using
+ valid_peer or valid as the second argument to the verify
+ fun. It may not always be trivial or even possible to
+ know when the peer certificate is reached otherwise.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8873</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Public_Key 0.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Handling of unknown CA certificates was changed in ssl
+ and public_key to work as intended.</p>
+ <p>
+ Own Id: OTP-8788</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Revise the public_key API - Cleaned up and documented the
+ public_key API to make it useful for general use, also
+ changed ssl to use the new API.</p>
+ <p>
+ Own Id: OTP-8722</p>
+ </item>
+ <item>
+ <p>
+ Added the functionality so that the verification fun will
+ be called when a certificate is considered valid by the
+ path validation to allow access to each certificate in
+ the path to the user application. Also try to verify
+ subject-AltName, if unable to verify it let the
+ application verify it.</p>
+ <p>
+ Own Id: OTP-8825</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Public_Key 0.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Certificates without any extensions could not be handled
+ by public_key.</p>
+ <p>
+ Own Id: OTP-8626</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Code cleanup and minor bugfixes.</p>
+ <p>
+ Own Id: OTP-8649</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 0.6</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 b85fa063ce..ea3123b5bd 100644
--- a/lib/public_key/doc/src/part.xml
+++ b/lib/public_key/doc/src/part.xml
@@ -1,11 +1,11 @@
-<?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">
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -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/part_notes.xml b/lib/public_key/doc/src/part_notes.xml
index 37ca516bc8..f855e76a6d 100644
--- a/lib/public_key/doc/src/part_notes.xml
+++ b/lib/public_key/doc/src/part_notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index dc9a96906f..9a3832c68b 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -34,11 +34,7 @@
<modulesummary> API module for public key infrastructure.</modulesummary>
<description>
<p>This module provides functions to handle public key infrastructure
- from RFC 3280 - X.509 certificates (will later be upgraded to RFC 5280)
- and some parts of the PKCS-standard.
- Currently this application is mainly used by the new
- ssl implementation. The API is yet under construction
- and only a few of the functions are currently documented and thereby supported.
+ from RFC 5280 - X.509 certificates and some parts of the PKCS-standard.
</p>
</description>
@@ -47,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>.
@@ -60,44 +56,41 @@
<p><em>Data Types </em></p>
- <p><c>boolean() = true | false</c></p>
-
- <p><c>string = [bytes()]</c></p>
-
- <p><c>asn1_der_encoded() = binary() | [bytes()]</c></p>
+ <p><code>boolean() = true | false</code></p>
- <p><c>der_bin() = binary() </c></p>
+ <p><code>string = [bytes()]</code></p>
+
+ <p><code>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey'
+ 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'</code></p>
- <p><c>oid() - a tuple of integers
- as generated by the asn1 compiler.</c></p>
-
- <p><c>public_key() = rsa_public_key() | dsa_public_key()</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><c>dsa_public_key() = integer() </c></p>
+ <p><code>rsa_private_key() = #'RSAPrivateKey'{} </code></p>
- <p><c>public_key_params() = dsa_key_params() </c></p>
-
- <p><c>dsa_key_params() = #'Dss-Parms'{} </c></p>
-
- <p><c>private_key() = rsa_private_key() | dsa_private_key()</c></p>
+ <p><code>dsa_public_key() = {integer(), #'Dss-Parms'{}} </code></p>
- <p><c>rsa_private_key() = #'RSAPrivateKey'{} </c></p>
+ <p><code>dsa_private_key() = #'DSAPrivateKey'{}</code></p>
- <p><c>dsa_private_key() = #'DSAPrivateKey'{}</c></p>
+ <p><code> public_crypt_options() = [{rsa_pad, rsa_padding()}]. </code></p>
- <p><c>x509_certificate() = "#Certificate{}"</c></p>
+ <p><code> rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
+ | 'rsa_no_padding'</code></p>
+
+ <p><code> rsa_digest_type() = 'md5' | 'sha' </code></p>
- <p><c>x509_tbs_certificate() = #'TBSCertificate'{} </c></p>
+ <p><code> dss_digest_type() = 'none' | 'sha' </code></p>
-<!-- <p><c>policy_tree() = [Root, Children]</c></p> -->
+ <p><code> ssh_file() = openssh_public_key | rfc4716_public_key |
+ known_hosts | auth_keys </code></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> -->
@@ -121,197 +114,359 @@
<!-- that would satisfy this policy in the certificate x+1. </item> -->
<!-- </taglist> -->
</section>
-
-<funcs>
- <func>
- <name>decode_private_key(KeyInfo) -> </name>
- <name>decode_private_key(KeyInfo, Password) -> {ok, PrivateKey} | {error, Reason}</name>
- <fsummary> Decodes an asn1 der encoded private key.</fsummary>
- <type>
- <v> KeyInfo = {KeyType, der_bin(), ChipherInfo} </v>
- <d> As returned from pem_to_der/1 for private keys</d>
- <v> KeyType = rsa_private_key | dsa_private_key </v>
- <v> ChipherInfo = opaque() | no_encryption </v>
- <d> ChipherInfo may contain encryption parameters if the private key is password
- protected, these are opaque to the user just pass the value returned by pem_to_der/1
- to this function.</d>
- <v> Password = string() </v>
- <d>Must be specified if CipherInfo =/= no_encryption</d>
- <v> PrivateKey = private_key() </v>
- <v> Reason = term() </v>
- </type>
- <desc>
- <p>Decodes an asn1 der encoded private key.</p>
- </desc>
+
+<funcs>
+
+ <func>
+ <name>decrypt_private(CipherText, Key [, Options]) -> binary()</name>
+ <fsummary>Public key decryption.</fsummary>
+ <type>
+ <v>CipherText = binary()</v>
+ <v>Key = rsa_private_key()</v>
+ <v>Options = public_crypt_options()</v>
+ </type>
+ <desc>
+ <p>Public key decryption using the private key.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>decrypt_public(CipherText, Key [, Options]) - > binary()</name>
+ <fsummary></fsummary>
+ <type>
+ <v>CipherText = binary()</v>
+ <v>Key = rsa_public_key()</v>
+ <v>Options = public_crypt_options()</v>
+ </type>
+ <desc>
+ <p> Public key decryption using the public key.</p>
+ </desc>
</func>
-
+
<func>
- <name>pem_to_der(File) -> {ok, [Entry]}</name>
- <fsummary>Reads a PEM file and translates it into its asn1 der
- encoded parts.</fsummary>
+ <name>der_decode(Asn1type, Der) -> term()</name>
+ <fsummary> Decodes a public key asn1 der encoded entity.</fsummary>
<type>
- <v>File = path()</v>
- <v>Password = string()</v>
- <v>Entry = {entry_type(), der_bin(), CipherInfo}</v>
- <v> ChipherInfo = opaque() | no_encryption </v>
- <d> ChipherInfo may contain encryption parameters if the private key is password
- protected, these will be handled by the function decode_private_key/2. </d>
- <v>entry_type() = cert | cert_req | rsa_private_key | dsa_private_key |
- dh_params </v>
+ <v>Asn1Type = atom()</v>
+ <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 ASN.1 der encoded entity.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>der_encode(Asn1Type, Entity) -> der_encoded()</name>
+ <fsummary> Encodes a public key entity with asn1 DER encoding.</fsummary>
+ <type>
+ <v>Asn1Type = atom()</v>
+ <d> Asn1 type present in the public_key applications
+ ASN.1 specifications.</d>
+ <v>Entity = term()</v>
+ <d>The erlang representation of <c>Asn1Type</c></d>
</type>
<desc>
- <p>Reads a PEM file and translates it into its asn1 der
- encoded parts.</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 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 ASN.1 der encoded entities.</p>
</desc>
</func>
- <func>
- <name>pkix_decode_cert(Cert, Type) -> {ok, DecodedCert} | {error, Reason}</name>
- <fsummary> Decodes an asn1 der encoded pkix certificate. </fsummary>
- <type>
- <v>Cert = asn1_der_encoded() </v>
- <v>Type = plain | otp</v>
- <v>DecodeCert = x509_certificate() </v>
- <d>When type is specified as otp the asn1 spec OTP-PKIX.asn1 is used to decode known
- extensions and enhance the signature field in
- #'Certificate'{} and '#TBSCertificate'{}. This is currently used by the new ssl
- implementation but not documented and supported for the public_key application.</d>
- <v>Reason = term() </v>
+ <func>
+ <name>pem_encode(PemEntries) -> binary()</name>
+ <fsummary>Creates a PEM binary</fsummary>
+ <type>
+ <v> PemEntries = [pem_entry()] </v>
+ </type>
+ <desc>
+ <p>Creates a PEM binary</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pem_entry_decode(PemEntry [, Password]) -> term()</name>
+ <fsummary>Decodes a pem entry.</fsummary>
+ <type>
+ <v> PemEntry = pem_entry() </v>
+ <v> Password = string() </v>
+ </type>
+ <desc>
+ <p>Decodes a pem entry. pem_decode/1 returns a list of pem
+ entries. Note that if the pem entry is of type
+ 'SubjectPublickeyInfo' it will be further decoded to an
+ rsa_public_key() or dsa_public_key().</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pem_entry_encode(Asn1Type, Entity [,{CipherInfo, Password}]) -> pem_entry()</name>
+ <fsummary> Creates a pem entry that can be fed to pem_encode/1.</fsummary>
+ <type>
+ <v>Asn1Type = pki_asn1_type()</v>
+ <v>Entity = term()</v>
+ <d>The Erlang representation of
+ <c>Asn1Type</c>. If <c>Asn1Type</c> is 'SubjectPublicKeyInfo'
+ then <c>Entity</c> must be either an rsa_public_key() or a
+ dsa_public_key() and this function will create the appropriate
+ 'SubjectPublicKeyInfo' entry.
+ </d>
+ <v>CipherInfo = {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}</v>
+ <v>Password = string()</v>
+ </type>
+ <desc>
+ <p> Creates a pem entry that can be feed to pem_encode/1.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>encrypt_private(PlainText, Key) -> binary()</name>
+ <fsummary> Public key encryption using the private key.</fsummary>
+ <type>
+ <v>PlainText = binary()</v>
+ <v>Key = rsa_private_key()</v>
+ </type>
+ <desc>
+ <p> Public key encryption using the private key.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>encrypt_public(PlainText, Key) -> binary()</name>
+ <fsummary> Public key encryption using the public key.</fsummary>
+ <type>
+ <v>PlainText = binary()</v>
+ <v>Key = rsa_public_key()</v>
+ </type>
+ <desc>
+ <p> Public key encryption using the public key.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_decode_cert(Cert, otp|plain) -> #'Certificate'{} | #'OTPCertificate'{}</name>
+ <fsummary> Decodes an ASN.1 der encoded pkix x509 certificate.</fsummary>
+ <type>
+ <v>Cert = der_encoded()</v>
+ </type>
+ <desc>
+ <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>
+ </func>
+
+ <func>
+ <name>pkix_encode(Asn1Type, Entity, otp | plain) -> der_encoded()</name>
+ <fsummary>Der encodes a pkix x509 certificate or part of such a
+ certificate.</fsummary>
+ <type>
+ <v>Asn1Type = atom()</v>
+ <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
+ certificate. This function must be used for encoding certificates or parts of certificates
+ that are decoded/created in the otp format, whereas for the plain format this
+ function will directly call der_encode/2. </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_is_issuer(Cert, IssuerCert) -> boolean()</name>
+ <fsummary> Checks if <c>IssuerCert</c> issued <c>Cert</c> </fsummary>
+ <type>
+ <v>Cert = der_encode() | #'OTPCertificate'{}</v>
+ <v>IssuerCert = der_encode() | #'OTPCertificate'{}</v>
+ </type>
+ <desc>
+ <p> Checks if <c>IssuerCert</c> issued <c>Cert</c> </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_is_fixed_dh_cert(Cert) -> boolean()</name>
+ <fsummary> Checks if a Certificate is a fixed Diffie-Hellman Cert.</fsummary>
+ <type>
+ <v>Cert = der_encode() | #'OTPCertificate'{}</v>
+ </type>
+ <desc>
+ <p> Checks if a Certificate is a fixed Diffie-Hellman Cert.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_is_self_signed(Cert) -> boolean()</name>
+ <fsummary> Checks if a Certificate is self signed.</fsummary>
+ <type>
+ <v>Cert = der_encode() | #'OTPCertificate'{}</v>
+ </type>
+ <desc>
+ <p> Checks if a Certificate is self signed.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_issuer_id(Cert, IssuedBy) -> {ok, IssuerID} | {error, Reason}</name>
+ <fsummary> Returns the issuer id.</fsummary>
+ <type>
+ <v>Cert = der_encode() | #'OTPCertificate'{}</v>
+ <v>IssuedBy = self | other</v>
+ <v>IssuerID = {integer(), {rdnSequence, [#'AttributeTypeAndValue'{}]}}</v>
+ <d>The issuer id consists of the serial number and the issuers name.</d>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p> Returns the issuer id.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_normalize_name(Issuer) -> Normalized</name>
+ <fsummary>Normalizes a issuer name so that it can be easily
+ compared to another issuer name. </fsummary>
+ <type>
+ <v>Issuer = {rdnSequence,[#'AttributeTypeAndValue'{}]}</v>
+ <v>Normalized = {rdnSequence, [#'AttributeTypeAndValue'{}]}</v>
+ </type>
+ <desc>
+ <p>Normalizes a issuer name so that it can be easily
+ compared to another issuer name.</p>
+ </desc>
+ </func>
+
+ <!-- <func> -->
+ <!-- <name>pkix_path_validation()</name> -->
+ <!-- <fsummary> Performs a basic path validation according to RFC 5280.</fsummary> -->
+ <!-- <type> -->
+ <!-- <v></v> -->
+ <!-- </type> -->
+ <!-- <desc> -->
+ <!-- <p> Performs a basic path validation according to RFC 5280.</p> -->
+ <!-- </desc> -->
+ <!-- </func> -->
+
+
+ <func>
+ <name>pkix_sign(#'OTPTBSCertificate'{}, Key) -> der_encode()</name>
+ <fsummary>Signs certificate.</fsummary>
+ <type>
+ <v>Key = rsa_public_key() | dsa_public_key()</v>
</type>
- <desc>
- <p> Decodes an asn1 encoded pkix certificate.</p>
+ <desc>
+ <p>Signs a 'OTPTBSCertificate'. Returns the corresponding
+ der encoded certificate.</p>
</desc>
+ </func>
+
+ <func>
+ <name>pkix_verify(Cert, Key) -> boolean()</name>
+ <fsummary> Verify pkix x.509 certificate signature.</fsummary>
+ <type>
+ <v>Cert = der_encode()</v>
+ <v>Key = rsa_public_key() | dsa_public_key()</v>
+ </type>
+ <desc>
+ <p> Verify pkix x.509 certificate signature.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>sign(Msg, DigestType, Key) -> binary()</name>
+ <fsummary> Create digital signature.</fsummary>
+ <type>
+ <v>Msg = binary()</v>
+ <d>The msg is either the binary "plain text" data to be
+ 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_private_key() | dsa_private_key()</v>
+ </type>
+ <desc>
+ <p> Creates a digital signature.</p>
+ </desc>
+ </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>
+ <v>Msg = binary()</v>
+ <d>The msg is either the binary "plain text" data
+ 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>Signature = binary()</v>
+ <v>Key = rsa_public_key() | dsa_public_key()</v>
+ </type>
+ <desc>
+ <p>Verifies a digital signature</p>
+ </desc>
</func>
-<!-- <func> -->
-<!-- <name> pkix_encode_cert(Cert) -> {ok, EncodedCert} | {error, Reason}</name> -->
-<!-- <fsummary>Encodes a certificate record using asn1. </fsummary> -->
-<!-- <type> -->
-<!-- <v>Cert = x509_certificate() </v> -->
-<!-- <v>EncodedCert = asn1_der_encoded() </v> -->
-<!-- <v>Reason = term() </v> -->
-<!-- </type> -->
-<!-- <desc> -->
-<!-- <p> Encodes a certificate record using asn1.</p> -->
-<!-- </desc> -->
-<!-- </func> -->
-
-<!-- <func> -->
-<!-- <name>pkix_path_validation(TrustedCert, CertChain, Options) -> {ok, Result} | {error, Reason}</name> -->
-
-<!-- <fsummary>Performs a basic path validation according to RFC 3280</fsummary> -->
-<!-- <type> -->
-<!-- <v>TrustedCert = asn1_der_encoded()</v> -->
-<!-- <v>CertChain = [asn1_der_encoded()]</v> -->
-<!-- <v>Options = [{Option, Value}]</v> -->
-<!-- <v>Result = {{algorithm(), public_key(), -->
-<!-- public_key_params()}, policy_tree()}</v> -->
-<!-- </type> -->
-
-<!-- <desc> -->
-<!-- <p>Available options are: </p> -->
-<!-- <taglist> -->
-<!-- <tag>{validate_extension_fun, fun()}</tag> -->
-<!-- <item> A fun behaving according to the following outline: -->
-<!-- <code> -->
-<!-- [...] -->
-<!-- ValidateExtensionFun = fun(Extensions, UserState) -> -->
-<!-- validate_extensions(Extensions, UserState, []) -->
-<!-- end, -->
-<!-- [...] -->
-
-<!-- validate_extensions([], UserState, UnknowExtension) -> -->
-<!-- {UserState, UnknowExtension}; -->
-<!-- validate_extensions([#'Extension'{} = Ext | Rest], UserState, UnknowExtension) -> -->
-<!-- case valid_extension(Ext) of -->
-<!-- {true, NewUserState} -> -->
-<!-- validate_extensions(Rest, NewUserState, UnknowExtension); -->
-<!-- unknown -> -->
-<!-- validate_extensions(Rest, UserState, [Ext | UnknowExtension]); -->
-<!-- {false, Reason} -> -->
-<!-- throw(bad_cert, Reason) -->
-<!-- end. -->
-<!-- </code> -->
-
-<!-- </item> -->
-
-<!-- <tag>{policy_set, [oid()]}</tag> -->
-<!-- <item>A set of certificate policy -->
-<!-- identifiers naming the policies that are acceptable to the -->
-<!-- certificate user. If the user is not concerned about -->
-<!-- certificate policy there is no need -->
-<!-- to set this option. Defaults to the -->
-<!-- special value [?anyPolicy]. -->
-<!-- </item> -->
-
-<!-- <tag>{policy_mapping, boolean()}</tag> -->
-<!-- <item>Indicates if policy -->
-<!-- mapping, initially, is allowed in the certification path. -->
-<!-- Defaults to false. -->
-<!-- </item> -->
-
-<!-- <tag> {explicit_policy, boolean()}</tag> -->
-<!-- <item>Indicates if the path, initially, must be -->
-<!-- valid for at least one of the certificate policies in the user -->
-<!-- specified policy set. -->
-<!-- Defaults to false. -->
-<!-- </item> -->
-
-<!-- <tag>{inhibit_any_policy, boolean()}</tag> -->
-<!-- <item>Indicates whether the anyPolicy OID, initially, should -->
-<!-- be processed if it is included in a certificate. -->
-<!-- Defaults to false. -->
-<!-- </item> -->
-
-<!-- </taglist> -->
-
-<!-- <p>Performs a basic path validation according to RFC 3280, -->
-<!-- e.i. signature validation, time validation, issuer validation, -->
-<!-- alternative subject name validation, CRL validation, policy -->
-<!-- validation and checks that no unknown extensions -->
-<!-- are marked as critical. The option <c>validate_extension_fun</c> -->
-<!-- may be used to validate application specific extensions. If -->
-<!-- a validation criteria is found to be invalid the validation process -->
-<!-- will immediately be stopped and this functions will return -->
-<!-- {error, Reason}. -->
-<!-- </p> -->
-<!-- </desc> -->
-<!-- </func> -->
-
-<!-- <func> -->
-<!-- <name>sign(DigestOrTBSCert, Key) -> </name> -->
-<!-- <name>sign(DigestOrTBSCert, Key, KeyParams) -> {ok, SignatureOrDerCert} | {error, Reason}</name> -->
-<!-- <fsummary>Signs Digest/Certificate using Key.</fsummary> -->
-<!-- <type> -->
-<!-- <v>DigestOrTBSCert = binary() | x509_tbs_certificate()</v> -->
-<!-- <v>Key = private_key()</v> -->
-<!-- <v>SignatureORDerCert = binary() | der_bin() </v> -->
-<!-- <v>Reason = term() </v> -->
-<!-- </type> -->
-<!-- <desc> -->
-<!-- <p> Signs Digest/Certificate using Key, in the later -->
-<!-- case a der encoded x509_certificate() will be returned. </p> -->
-<!-- </desc> -->
-<!-- </func> -->
-
-<!-- <func> -->
-<!-- <name>verify_signature(Digest, Signature, Key) -> </name> -->
-<!-- <name>verify_signature(DerCert, Key, KeyParams) -> </name> -->
-<!-- <name>verify_signature(Digest, Signature, Key, Params) -> Verified </name> -->
-<!-- <fsummary> Verifies the signature. </fsummary> -->
-<!-- <type> -->
-<!-- <v>Digest = binary() </v> -->
-<!-- <v>DerCert = der_bin() </v> -->
-<!-- <v>Signature = binary() </v> -->
-<!-- <v>Key = public_key() </v> -->
-<!-- <v>Params = key_params()</v> -->
-<!-- <v>Verified = boolean()</v> -->
-<!-- </type> -->
-<!-- <desc> -->
-<!-- <p> Verifies the signature Signature. If the key is an rsa-key no -->
-<!-- paramters are neeed.</p> -->
-<!-- </desc> -->
-<!-- </func> -->
</funcs>
</erlref>
diff --git a/lib/public_key/doc/src/public_key_records.xml b/lib/public_key/doc/src/public_key_records.xml
index 45b7106859..bb90290266 100644
--- a/lib/public_key/doc/src/public_key_records.xml
+++ b/lib/public_key/doc/src/public_key_records.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/public_key/doc/src/ref_man.xml b/lib/public_key/doc/src/ref_man.xml
index 0f11281d05..285cc36c6f 100644
--- a/lib/public_key/doc/src/ref_man.xml
+++ b/lib/public_key/doc/src/ref_man.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
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>&lt;text&gt;
+ -----BEGIN &lt;SOMETHING&gt;-----
+ &lt;Attribute&gt; : &lt;Value&gt;
+ &lt;Base64 encoded DER data&gt;
+ -----END &lt;SOMETHING&gt;-----
+ &lt;text&gt;</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,&lt;&lt;"-----BEGIN DSA PRIVATE KEY-----\nMIIBuw"...&gt;&gt;}</code>
+
+ <p>This PEM file only has one entry a private DSA key.</p>
+ <code>2> [DSAEntry] = public_key:pem_decode(PemBin).
+[{'DSAPrivateKey',&lt;&lt;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,...&gt;&gt;,
+ 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,&lt;&lt;"Bag Attribut"...&gt;&gt;}</code>
+
+ <p>This PEM file only has one entry a private RSA key.</p>
+ <code>2>[RSAEntry] = public_key:pem_decode(PemBin).
+[{'RSAPrivateKey',&lt;&lt;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,...&gt;&gt;,
+ {"DES-EDE3-CBC",&lt;&lt;"k�e��p�L"&gt;&gt;}}]
+
+ </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,&lt;&lt;"-----BEGIN CERTIFICATE-----\nMIIC7jCCAl"...&gt;&gt;}</code>
+
+ <p>This file includes two certificates</p>
+ <code>2> [CertEntry1, CertEntry2] = public_key:pem_decode(PemBin).
+[{'Certificate',&lt;&lt;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,
+ ...&gt;&gt;,
+ not_encrypted},
+ {'Certificate',&lt;&lt;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,...&gt;&gt;>,
+ 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 = &lt;&lt;5,0&gt;&gt;},
+ issuer =
+ {rdnSequence,
+ [[#'AttributeTypeAndValue'{
+ type = {2,5,4,3},
+ value = &lt;&lt;19,8,101,114,108,97,110,103,67,65&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,11},
+ value = &lt;&lt;19,10,69,114,108,97,110,103,32,79,84,80&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,10},
+ value = &lt;&lt;19,11,69,114,105,99,115,115,111,110,32,65,66&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,7},
+ value = &lt;&lt;19,9,83,116,111,99,107,104,111,108,109&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,6},
+ value = &lt;&lt;19,2,83,69&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {1,2,840,113549,1,9,1},
+ value = &lt;&lt;22,22,112,101,116,101,114,64,101,114,...&gt;&gt;}]]},
+ validity =
+ #'Validity'{
+ notBefore = {utcTime,"080109082929Z"},
+ notAfter = {utcTime,"080208082929Z"}},
+ subject =
+ {rdnSequence,
+ [[#'AttributeTypeAndValue'{
+ type = {2,5,4,3},
+ value = &lt;&lt;19,8,101,114,108,97,110,103,67,65&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,11},
+ value = &lt;&lt;19,10,69,114,108,97,110,103,32,79,84,80&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,10},
+ value = &lt;&lt;19,11,69,114,105,99,115,115,111,110,32,...&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,7},
+ value = &lt;&lt;19,9,83,116,111,99,107,104,111,108,...&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,6},
+ value = &lt;&lt;19,2,83,69&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {1,2,840,113549,1,9,1},
+ value = &lt;&lt;22,22,112,101,116,101,114,64,...&gt;&gt;}]]},
+ subjectPublicKeyInfo =
+ #'SubjectPublicKeyInfo'{
+ algorithm =
+ #'AlgorithmIdentifier'{
+ algorithm = {1,2,840,113549,1,1,1},
+ parameters = &lt;&lt;5,0&gt;&gt;},
+ subjectPublicKey =
+ {0,&lt;&lt;48,129,137,2,129,129,0,203,209,187,77,73,231,90,...&gt;&gt;}},
+ 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 = &lt;&lt;5,0&gt;&gt;},
+ signature =
+ {0,
+ &lt;&lt;163,186,7,163,216,152,63,47,154,234,139,73,154,96,120,
+ 165,2,52,196,195,109,167,192,...&gt;&gt;}}
+</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', &lt;&lt;19,8,101,114,108,97,110,103,67,65&gt;&gt;).
+{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,
+ &lt;&lt;163,186,7,163,216,152,63,47,154,234,139,73,154,96,120,
+ 165,2,52,196,195,109,167,192,...&gt;&gt;}}
+</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', &lt;&lt;48,72,...&gt;&gt;, not_encrypted}
+
+2> PemBin = public_key:pem_encode([PemEntry]).
+&lt;&lt;"-----BEGIN RSA PUBLIC KEY-----\nMEgC...&gt;&gt;
+
+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', &lt;&lt;48,92...&gt;&gt;, not_encrypted}
+
+2> PemBin = public_key:pem_encode([PemEntry]).
+&lt;&lt;"-----BEGIN PUBLIC KEY-----\nMFw...&gt;&gt;
+
+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, &lt;&lt;"---- BEGIN SSH2 PUBLIC KEY ----\nAAAA"...&gt;&gt;}</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,&lt;&lt;"ssh-dss AAAAB3Nza"...&gt;&gt;}</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,&lt;&lt;"hostname.domain.com,192.168.0.1 ssh-rsa AAAAB...&gt;&gt;}</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, &lt;&lt;"command=\"dump /home\",no-pty,no-port-forwarding ssh-rsa AAA...&gt;&gt;}</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),
+&lt;&lt;"ssh-rsa "...&gt;&gt;
+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 fbce10f0eb..5f97d80f7e 100644
--- a/lib/public_key/include/public_key.hrl
+++ b/lib/public_key/include/public_key.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. 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
@@ -28,6 +28,17 @@
algorithm,
parameters = asn1_NOVALUE}).
+-define(DEFAULT_VERIFYFUN,
+ {fun(_,{bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []}).
+
-record(path_validation_state, {
valid_policy_tree,
explicit_policy,
@@ -42,7 +53,7 @@
working_public_key_parameters,
working_issuer_name,
max_path_length,
- acc_errors, %% If verify_none option is set
+ verify_fun,
user_state
}).
@@ -59,4 +70,18 @@
interim_reasons_mask
}).
+-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(), 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 c30399f33a..5a24b02d2a 100644
--- a/lib/public_key/src/Makefile
+++ b/lib/public_key/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2009. 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
@@ -41,9 +41,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/public_key-$(VSN)
MODULES = \
public_key \
pubkey_pem \
+ pubkey_ssh \
pubkey_cert \
- pubkey_cert_records \
- pubkey_crypto
+ pubkey_cert_records
HRL_FILES = $(INCLUDE)/public_key.hrl
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index 8f7dfa8352..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
@@ -23,14 +23,13 @@
-include("public_key.hrl").
--export([verify_signature/3,
- init_validation_state/3, prepare_for_next_cert/2,
+-export([init_validation_state/3, prepare_for_next_cert/2,
validate_time/3, validate_signature/6,
validate_issuer/4, validate_names/6,
validate_revoked_status/3, validate_extensions/4,
- validate_unknown_extensions/3,
- normalize_general_name/1, digest_type/1, digest/2, is_self_signed/1,
- is_issuer/2, issuer_id/2, is_fixed_dh_cert/1]).
+ normalize_general_name/1, digest_type/1, is_self_signed/1,
+ is_issuer/2, issuer_id/2, is_fixed_dh_cert/1,
+ verify_data/1, verify_fun/4]).
-define(NULL, 0).
@@ -38,10 +37,22 @@
%% Internal application API
%%====================================================================
-verify_signature(DerCert, Key, KeyParams) ->
- {ok, OtpCert} = pubkey_cert_records:decode_cert(DerCert, otp),
- verify_signature(OtpCert, DerCert, Key, KeyParams).
+%%--------------------------------------------------------------------
+-spec verify_data(DER::binary()) -> {md5 | sha, binary(), binary()}.
+%%
+%% Description: Extracts data from DerCert needed to call public_key:verify/4.
+%%--------------------------------------------------------------------
+verify_data(DerCert) ->
+ {ok, OtpCert} = pubkey_cert_records:decode_cert(DerCert),
+ extract_verify_data(OtpCert, DerCert).
+%%--------------------------------------------------------------------
+-spec init_validation_state(#'OTPCertificate'{}, integer(), list()) ->
+ #path_validation_state{}.
+%%
+%% Description: Creates inital version of path_validation_state for
+%% basic path validation of x509 certificates.
+%%--------------------------------------------------------------------
init_validation_state(#'OTPCertificate'{} = OtpCert, DefaultPathLen,
Options) ->
PolicyTree = #policy_tree_node{valid_policy = ?anyPolicy,
@@ -56,16 +67,23 @@ init_validation_state(#'OTPCertificate'{} = OtpCert, DefaultPathLen,
Options, false)),
PolicyMapping = policy_indicator(MaxLen,
proplists:get_value(policy_mapping, Options, false)),
- AccErrors = proplists:get_value(acc_errors, Options, []),
- State = #path_validation_state{max_path_length = MaxLen,
- valid_policy_tree = PolicyTree,
- explicit_policy = ExplicitPolicy,
- inhibit_any_policy = InhibitAnyPolicy,
- policy_mapping = PolicyMapping,
- acc_errors = AccErrors,
+ {VerifyFun, UserState} = proplists:get_value(verify_fun, Options, ?DEFAULT_VERIFYFUN),
+ State = #path_validation_state{max_path_length = MaxLen,
+ valid_policy_tree = PolicyTree,
+ explicit_policy = ExplicitPolicy,
+ inhibit_any_policy = InhibitAnyPolicy,
+ policy_mapping = PolicyMapping,
+ verify_fun = VerifyFun,
+ user_state = UserState,
cert_num = 0},
prepare_for_next_cert(OtpCert, State).
+%%--------------------------------------------------------------------
+-spec prepare_for_next_cert(#'OTPCertificate'{}, #path_validation_state{}) ->
+ #path_validation_state{}.
+%%
+%% Description: Update path_validation_state for next iteration.
+%%--------------------------------------------------------------------
prepare_for_next_cert(OtpCert, ValidationState = #path_validation_state{
working_public_key_algorithm = PrevAlgo,
working_public_key_parameters =
@@ -92,8 +110,14 @@ prepare_for_next_cert(OtpCert, ValidationState = #path_validation_state{
working_issuer_name = Issuer,
cert_num = ValidationState#path_validation_state.cert_num + 1
}.
-
-validate_time(OtpCert, AccErr, Verify) ->
+
+ %%--------------------------------------------------------------------
+-spec validate_time(#'OTPCertificate'{}, term(), fun()) -> term().
+%%
+%% Description: Check that the certificate validity period includes the
+%% current time.
+%%--------------------------------------------------------------------
+validate_time(OtpCert, UserState, VerifyFun) ->
TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
{'Validity', NotBeforeStr, NotAfterStr}
= TBSCert#'OTPTBSCertificate'.validity,
@@ -103,47 +127,68 @@ validate_time(OtpCert, AccErr, Verify) ->
case ((NotBefore =< Now) and (Now =< NotAfter)) of
true ->
- AccErr;
+ UserState;
false ->
- not_valid({bad_cert, cert_expired}, Verify, AccErr)
+ verify_fun(OtpCert, {bad_cert, cert_expired}, UserState, VerifyFun)
end.
-
-validate_issuer(OtpCert, Issuer, AccErr, Verify) ->
+%%--------------------------------------------------------------------
+-spec validate_issuer(#'OTPCertificate'{}, term(), term(), fun()) -> term().
+%%
+%% Description: Check that the certificate issuer name is the working_issuer_name
+%% in path_validation_state.
+%%--------------------------------------------------------------------
+validate_issuer(OtpCert, Issuer, UserState, VerifyFun) ->
TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
case is_issuer(Issuer, TBSCert#'OTPTBSCertificate'.issuer) of
true ->
- AccErr;
+ UserState;
_ ->
- not_valid({bad_cert, invalid_issuer}, Verify, AccErr)
+ verify_fun(OtpCert, {bad_cert, invalid_issuer}, UserState, VerifyFun)
end.
-
+%%--------------------------------------------------------------------
+-spec validate_signature(#'OTPCertificate'{}, DER::binary(),
+ term(),term(), term(), fun()) -> term().
+
+%%
+%% Description: Check that the signature on the certificate can be verified using
+%% working_public_key_algorithm, the working_public_key, and
+%% the working_public_key_parameters in path_validation_state.
+%%--------------------------------------------------------------------
validate_signature(OtpCert, DerCert, Key, KeyParams,
- AccErr, Verify) ->
+ UserState, VerifyFun) ->
case verify_signature(OtpCert, DerCert, Key, KeyParams) of
true ->
- AccErr;
+ UserState;
false ->
- not_valid({bad_cert, invalid_signature}, Verify, AccErr)
+ verify_fun(OtpCert, {bad_cert, invalid_signature}, UserState, VerifyFun)
end.
-
-validate_names(OtpCert, Permit, Exclude, Last, AccErr, Verify) ->
+%%--------------------------------------------------------------------
+-spec validate_names(#'OTPCertificate'{}, no_constraints | list(), list(),
+ term(), term(), fun())-> term().
+%%
+%% Description: Validate Subject Alternative Name.
+%%--------------------------------------------------------------------
+validate_names(OtpCert, Permit, Exclude, Last, UserState, VerifyFun) ->
case is_self_signed(OtpCert) andalso (not Last) of
true ->
- ok;
+ UserState;
false ->
TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
Subject = TBSCert#'OTPTBSCertificate'.subject,
+ Extensions =
+ extensions_list(TBSCert#'OTPTBSCertificate'.extensions),
AltSubject =
- select_extension(?'id-ce-subjectAltName',
- TBSCert#'OTPTBSCertificate'.extensions),
+ select_extension(?'id-ce-subjectAltName', Extensions),
EmailAddress = extract_email(Subject),
Name = [{directoryName, Subject}|EmailAddress],
AltNames = case AltSubject of
- undefined -> [];
- _ -> AltSubject#'Extension'.extnValue
+ undefined ->
+ [];
+ _ ->
+ AltSubject#'Extension'.extnValue
end,
case (is_permitted(Name, Permit) andalso
@@ -151,68 +196,77 @@ validate_names(OtpCert, Permit, Exclude, Last, AccErr, Verify) ->
(not is_excluded(Name, Exclude)) andalso
(not is_excluded(AltNames, Exclude))) of
true ->
- AccErr;
+ UserState;
false ->
- not_valid({bad_cert, name_not_permitted},
- Verify, AccErr)
+ verify_fun(OtpCert, {bad_cert, name_not_permitted},
+ UserState, VerifyFun)
end
end.
-
-%% See rfc3280 4.1.2.6 Subject: regarding emails.
-extract_email({rdnSequence, List}) ->
- extract_email2(List).
-extract_email2([[#'AttributeTypeAndValue'{type=?'id-emailAddress',
- value=Mail}]|_]) ->
- [{rfc822Name, Mail}];
-extract_email2([_|Rest]) ->
- extract_email2(Rest);
-extract_email2([]) -> [].
-
-validate_revoked_status(_OtpCert, _Verify, AccErr) ->
- %% true |
+%%--------------------------------------------------------------------
+-spec validate_revoked_status(#'OTPCertificate'{}, term(), fun()) ->
+ term().
+%%
+%% Description: Check if certificate has been revoked.
+%%--------------------------------------------------------------------
+validate_revoked_status(_OtpCert, UserState, _VerifyFun) ->
+ %% TODO: Implement or leave for application?!
+ %% valid |
%% throw({bad_cert, cert_revoked})
- AccErr.
-
-validate_extensions(OtpCert, ValidationState, Verify, AccErr) ->
+ UserState.
+%%--------------------------------------------------------------------
+-spec validate_extensions(#'OTPCertificate'{}, #path_validation_state{},
+ term(), fun())->
+ {#path_validation_state{}, UserState :: term()}.
+%%
+%% Description: Check extensions included in basic path validation.
+%%--------------------------------------------------------------------
+validate_extensions(OtpCert, ValidationState, UserState, VerifyFun) ->
TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
- Extensions = TBSCert#'OTPTBSCertificate'.extensions,
- validate_extensions(Extensions, ValidationState, no_basic_constraint,
- is_self_signed(OtpCert), [], Verify, AccErr).
-
-validate_unknown_extensions([], AccErr, _Verify) ->
- AccErr;
-validate_unknown_extensions([#'Extension'{critical = true} | _],
- AccErr, Verify) ->
- not_valid({bad_cert, unknown_critical_extension}, Verify, AccErr);
-validate_unknown_extensions([#'Extension'{critical = false} | Rest],
- AccErr, Verify) ->
- validate_unknown_extensions(Rest, AccErr, Verify).
-
+ case TBSCert#'OTPTBSCertificate'.version of
+ N when N >= 3 ->
+ Extensions = TBSCert#'OTPTBSCertificate'.extensions,
+ validate_extensions(OtpCert, Extensions,
+ ValidationState, no_basic_constraint,
+ is_self_signed(OtpCert), UserState, VerifyFun);
+ _ -> %% Extensions not present in versions 1 & 2
+ {ValidationState, UserState}
+ end.
+%%--------------------------------------------------------------------
+-spec normalize_general_name({rdnSequence, term()}) -> {rdnSequence, term()}.
+%%
+%% Description: Normalizes a general name so that it can be easily
+%% compared to another genral name.
+%%--------------------------------------------------------------------
normalize_general_name({rdnSequence, Issuer}) ->
- NormIssuer = normalize_general_name(Issuer),
- {rdnSequence, NormIssuer};
-
-normalize_general_name(Issuer) ->
- Normalize = fun([{Description, Type, {printableString, Value}}]) ->
- NewValue = string:to_lower(strip_spaces(Value)),
- {Description, Type, {printableString, NewValue}};
- (Atter) ->
- Atter
- end,
- lists:sort(lists:map(Normalize, Issuer)).
+ NormIssuer = do_normalize_general_name(Issuer),
+ {rdnSequence, NormIssuer}.
+%%--------------------------------------------------------------------
+-spec is_self_signed(#'OTPCertificate'{}) -> boolean().
+%%
+%% Description: Checks if the certificate is self signed.
+%%--------------------------------------------------------------------
is_self_signed(#'OTPCertificate'{tbsCertificate=
#'OTPTBSCertificate'{issuer = Issuer,
subject = Subject}}) ->
is_issuer(Issuer, Subject).
-
+%%--------------------------------------------------------------------
+-spec is_issuer({rdnSequence, term()}, {rdnSequence, term()}) -> boolean().
+%%
+%% Description: Checks if <Issuer> issued <Candidate>.
+%%--------------------------------------------------------------------
is_issuer({rdnSequence, Issuer}, {rdnSequence, Candidate}) ->
is_dir_name(Issuer, Candidate, true).
-
+%%--------------------------------------------------------------------
+-spec issuer_id(#'OTPCertificate'{}, self | other) ->
+ {ok, {integer(), term()}} | {error, issuer_not_found}.
+%%
+%% Description: Extracts the issuer id from a certificate if possible.
+%%--------------------------------------------------------------------
issuer_id(Otpcert, other) ->
TBSCert = Otpcert#'OTPCertificate'.tbsCertificate,
- Extensions = TBSCert#'OTPTBSCertificate'.extensions,
+ Extensions = extensions_list(TBSCert#'OTPTBSCertificate'.extensions),
case select_extension(?'id-ce-authorityKeyIdentifier', Extensions) of
undefined ->
{error, issuer_not_found};
@@ -226,34 +280,92 @@ issuer_id(Otpcert, self) ->
SerialNr = TBSCert#'OTPTBSCertificate'.serialNumber,
{ok, {SerialNr, normalize_general_name(Issuer)}}.
-
+%%--------------------------------------------------------------------
+-spec is_fixed_dh_cert(#'OTPCertificate'{}) -> boolean().
+%%
+%% Description: Checks if the certificate can be be used
+%% for DH key agreement.
+%%--------------------------------------------------------------------
is_fixed_dh_cert(#'OTPCertificate'{tbsCertificate =
#'OTPTBSCertificate'{subjectPublicKeyInfo =
SubjectPublicKeyInfo,
extensions =
Extensions}}) ->
- is_fixed_dh_cert(SubjectPublicKeyInfo, Extensions).
+ is_fixed_dh_cert(SubjectPublicKeyInfo, extensions_list(Extensions)).
+
+
+%%--------------------------------------------------------------------
+-spec verify_fun(#'OTPCertificate'{}, {bad_cert, atom()} | {extension, #'Extension'{}}|
+ valid | valid_peer, term(), fun()) -> term().
+%%
+%% Description: Gives the user application the opportunity handle path
+%% validation errors and unknown extensions and optional do other
+%% things with a validated certificate.
+%% --------------------------------------------------------------------
+verify_fun(Otpcert, Result, UserState0, VerifyFun) ->
+ case VerifyFun(Otpcert, Result, UserState0) of
+ {valid,UserState} ->
+ UserState;
+ {fail, Reason} ->
+ case Result of
+ {bad_cert, _} ->
+ throw(Result);
+ _ ->
+ throw({bad_cert, Reason})
+ end;
+ {unknown, UserState} ->
+ case Result of
+ {extension, #'Extension'{critical = true}} ->
+ throw({bad_cert, unknown_critical_extension});
+ _ ->
+ UserState
+ end
+ end.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+do_normalize_general_name(Issuer) ->
+ Normalize = fun([{Description, Type, {printableString, Value}}]) ->
+ NewValue = string:to_lower(strip_spaces(Value)),
+ [{Description, Type, {printableString, NewValue}}];
+ (Atter) ->
+ Atter
+ end,
+ lists:sort(lists:map(Normalize, Issuer)).
-not_valid(Error, true, _) ->
- throw(Error);
-not_valid(Error, false, AccErrors) ->
- [Error | AccErrors].
+%% See rfc3280 4.1.2.6 Subject: regarding emails.
+extract_email({rdnSequence, List}) ->
+ extract_email2(List).
+extract_email2([[#'AttributeTypeAndValue'{type=?'id-emailAddress',
+ value=Mail}]|_]) ->
+ [{rfc822Name, Mail}];
+extract_email2([_|Rest]) ->
+ extract_email2(Rest);
+extract_email2([]) -> [].
-verify_signature(OtpCert, DerCert, Key, KeyParams) ->
- %% Signature is an ASN1 compact bit string
+extensions_list(asn1_NOVALUE) ->
+ [];
+extensions_list(Extensions) ->
+ Extensions.
+
+
+extract_verify_data(OtpCert, DerCert) ->
{0, Signature} = OtpCert#'OTPCertificate'.signature,
SigAlgRec = OtpCert#'OTPCertificate'.signatureAlgorithm,
SigAlg = SigAlgRec#'SignatureAlgorithm'.algorithm,
- EncTBSCert = encoded_tbs_cert(DerCert),
- verify(SigAlg, EncTBSCert, Signature, Key, KeyParams).
+ PlainText = encoded_tbs_cert(DerCert),
+ DigestType = digest_type(SigAlg),
+ {DigestType, PlainText, Signature}.
-verify(Alg, PlainText, Signature, Key, KeyParams) ->
- public_key:verify_signature(PlainText, digest_type(Alg),
- Signature, Key, KeyParams).
+verify_signature(OtpCert, DerCert, Key, KeyParams) ->
+ {DigestType, PlainText, Signature} = extract_verify_data(OtpCert, DerCert),
+ case Key of
+ #'RSAPublicKey'{} ->
+ public_key:verify(PlainText, DigestType, Signature, Key);
+ _ ->
+ public_key:verify(PlainText, DigestType, Signature, {Key, KeyParams})
+ end.
encoded_tbs_cert(Cert) ->
{ok, PKIXCert} =
@@ -269,13 +381,6 @@ digest_type(?md5WithRSAEncryption) ->
digest_type(?'id-dsa-with-sha1') ->
sha.
-digest(?sha1WithRSAEncryption, Msg) ->
- crypto:sha(Msg);
-digest(?md5WithRSAEncryption, Msg) ->
- crypto:md5(Msg);
-digest(?'id-dsa-with-sha1', Msg) ->
- crypto:sha(Msg).
-
public_key_info(PublicKeyInfo,
#path_validation_state{working_public_key_algorithm =
WorkingAlgorithm,
@@ -289,10 +394,12 @@ public_key_info(PublicKeyInfo,
NewPublicKeyParams =
case PublicKeyParams of
- 'NULL' when WorkingAlgorithm == Algorithm ->
+ {null, 'NULL'} when WorkingAlgorithm == Algorithm ->
WorkingParams;
- _ ->
- PublicKeyParams
+ {params, Params} ->
+ Params;
+ Params ->
+ Params
end,
{Algorithm, PublicKey, NewPublicKeyParams}.
@@ -326,12 +433,6 @@ is_dir_name([[{'AttributeTypeAndValue', Type, What1}]|Rest1],
true -> is_dir_name(Rest1,Rest2,Exact);
false -> false
end;
-is_dir_name([{'AttributeTypeAndValue', Type, What1}|Rest1],
- [{'AttributeTypeAndValue', Type, What2}|Rest2], Exact) ->
- case is_dir_name2(What1,What2) of
- true -> is_dir_name(Rest1,Rest2,Exact);
- false -> false
- end;
is_dir_name(_,[],false) ->
true;
is_dir_name(_,_,_) ->
@@ -376,210 +477,179 @@ select_extension(Id, [_ | Extensions]) ->
select_extension(Id, Extensions).
%% No extensions present
-validate_extensions(asn1_NOVALUE, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr) ->
- validate_extensions([], ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
-validate_extensions([], ValidationState, basic_constraint, _SelfSigned,
- UnknownExtensions, _Verify, AccErr) ->
- {ValidationState, UnknownExtensions, AccErr};
-validate_extensions([], ValidationState =
- #path_validation_state{max_path_length = Len,
- last_cert = Last},
- no_basic_constraint, SelfSigned, UnknownExtensions,
- Verify, AccErr0) ->
+validate_extensions(OtpCert, asn1_NOVALUE, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun) ->
+ validate_extensions(OtpCert, [], ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
+
+validate_extensions(_,[], ValidationState, basic_constraint, _SelfSigned,
+ UserState, _) ->
+ {ValidationState, UserState};
+validate_extensions(OtpCert, [], ValidationState =
+ #path_validation_state{max_path_length = Len,
+ last_cert = Last},
+ no_basic_constraint, SelfSigned, UserState0, VerifyFun) ->
case Last of
true when SelfSigned ->
- {ValidationState, UnknownExtensions, AccErr0};
+ {ValidationState, UserState0};
true ->
{ValidationState#path_validation_state{max_path_length = Len - 1},
- UnknownExtensions, AccErr0};
+ UserState0};
%% basic_constraint must appear in certs used for digital sign
%% see 4.2.1.10 in rfc 3280
false ->
- AccErr = not_valid({bad_cert, missing_basic_constraint},
- Verify, AccErr0),
+ UserState = verify_fun(OtpCert, {bad_cert, missing_basic_constraint},
+ UserState0, VerifyFun),
case SelfSigned of
true ->
- {ValidationState, UnknownExtensions, AccErr};
+ {ValidationState, UserState};
false ->
{ValidationState#path_validation_state{max_path_length =
- Len - 1},
- UnknownExtensions, AccErr}
+ Len - 1},
+ UserState}
end
end;
-validate_extensions([#'Extension'{extnID = ?'id-ce-basicConstraints',
+validate_extensions(OtpCert,
+ [#'Extension'{extnID = ?'id-ce-basicConstraints',
extnValue =
- #'BasicConstraints'{cA = true,
- pathLenConstraint = N}} |
+ #'BasicConstraints'{cA = true,
+ pathLenConstraint = N}} |
Rest],
- ValidationState =
- #path_validation_state{max_path_length = Len}, _,
- SelfSigned, UnknownExtensions, Verify, AccErr) ->
- Length = if SelfSigned -> min(N, Len);
- true -> min(N, Len-1)
+ ValidationState =
+ #path_validation_state{max_path_length = Len}, _,
+ SelfSigned, UserState, VerifyFun) ->
+ Length = if SelfSigned -> erlang:min(N, Len);
+ true -> erlang:min(N, Len-1)
end,
- validate_extensions(Rest,
+ validate_extensions(OtpCert, Rest,
ValidationState#path_validation_state{max_path_length =
- Length},
- basic_constraint, SelfSigned, UnknownExtensions,
- Verify, AccErr);
+ Length},
+ basic_constraint, SelfSigned,
+ UserState, VerifyFun);
%% The pathLenConstraint field is meaningful only if cA is set to
%% TRUE.
-validate_extensions([#'Extension'{extnID = ?'id-ce-basicConstraints',
- extnValue =
- #'BasicConstraints'{cA = false}} |
- Rest], ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr) ->
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
-%%
-validate_extensions([#'Extension'{extnID = ?'id-ce-keyUsage',
- extnValue = KeyUse
- } | Rest],
- #path_validation_state{last_cert=Last} = ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr0) ->
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue =
+ #'BasicConstraints'{cA = false}} |
+ Rest], ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun) ->
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
+
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-keyUsage',
+ extnValue = KeyUse
+ } | Rest],
+ #path_validation_state{last_cert=Last} = ValidationState,
+ ExistBasicCon, SelfSigned,
+ UserState0, VerifyFun) ->
case Last orelse is_valid_key_usage(KeyUse, keyCertSign) of
true ->
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify,
- AccErr0);
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState0, VerifyFun);
false ->
- AccErr = not_valid({bad_cert, invalid_key_usage}, Verify, AccErr0),
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify,
- AccErr)
+ UserState = verify_fun(OtpCert, {bad_cert, invalid_key_usage},
+ UserState0, VerifyFun),
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun)
end;
-validate_extensions([#'Extension'{extnID = ?'id-ce-subjectAltName',
- extnValue = Names} | Rest],
- ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr0) ->
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-subjectAltName',
+ extnValue = Names,
+ critical = true} = Ext | Rest],
+ ValidationState, ExistBasicCon,
+ SelfSigned, UserState0, VerifyFun) ->
case validate_subject_alt_names(Names) of
- true when Names =/= [] ->
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify,
- AccErr0);
- _ ->
- AccErr =
- not_valid({bad_cert, invalid_subject_altname},
- Verify, AccErr0),
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify,
- AccErr)
+ true ->
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState0, VerifyFun);
+ false ->
+ UserState = verify_fun(OtpCert, {extension, Ext},
+ UserState0, VerifyFun),
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun)
end;
-%% This extension SHOULD NOT be marked critical. Its value
-%% does not have to be further validated at this point.
-validate_extensions([#'Extension'{extnID = ?'id-ce-issuerAltName',
- extnValue = _} | Rest],
- ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr) ->
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
-%% This extension MUST NOT be marked critical.Its value
-%% does not have to be further validated at this point.
-validate_extensions([#'Extension'{extnID = Id,
- extnValue = _,
- critical = false} | Rest],
- ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr)
- when Id == ?'id-ce-subjectKeyIdentifier';
- Id == ?'id-ce-authorityKeyIdentifier'->
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
-validate_extensions([#'Extension'{extnID = ?'id-ce-nameConstraints',
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-nameConstraints',
extnValue = NameConst} | Rest],
ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr) ->
+ ExistBasicCon, SelfSigned, UserState, VerifyFun) ->
Permitted = NameConst#'NameConstraints'.permittedSubtrees,
Excluded = NameConst#'NameConstraints'.excludedSubtrees,
NewValidationState = add_name_constraints(Permitted, Excluded,
ValidationState),
- validate_extensions(Rest, NewValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
+ validate_extensions(OtpCert, Rest, NewValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
-validate_extensions([#'Extension'{extnID = ?'id-ce-certificatePolicies',
- critical = true} | Rest], ValidationState,
- ExistBasicCon, SelfSigned,
- UnknownExtensions, Verify, AccErr0) ->
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-certificatePolicies',
+ critical = true} = Ext| Rest], ValidationState,
+ ExistBasicCon, SelfSigned, UserState0, VerifyFun) ->
%% TODO: Remove this clause when policy handling is
%% fully implemented
- AccErr =
- not_valid({bad_cert, unknown_critical_extension}, Verify, AccErr0),
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
-validate_extensions([#'Extension'{extnID = ?'id-ce-certificatePolicies',
- extnValue = #'PolicyInformation'{
- policyIdentifier = Id,
- policyQualifiers = Qualifier}}
- | Rest], #path_validation_state{valid_policy_tree = Tree}
+ UserState = verify_fun(OtpCert, {extension, Ext},
+ UserState0, VerifyFun),
+ validate_extensions(OtpCert,Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
+
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-certificatePolicies',
+ extnValue = #'PolicyInformation'{
+ policyIdentifier = Id,
+ policyQualifiers = Qualifier}}
+ | Rest], #path_validation_state{valid_policy_tree = Tree}
= ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr) ->
+ ExistBasicCon, SelfSigned, UserState, VerifyFun) ->
%% TODO: Policy imp incomplete
NewTree = process_policy_tree(Id, Qualifier, Tree),
- validate_extensions(Rest,
+ validate_extensions(OtpCert, Rest,
ValidationState#path_validation_state{
valid_policy_tree = NewTree},
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr);
+ ExistBasicCon, SelfSigned, UserState, VerifyFun);
-validate_extensions([#'Extension'{extnID = ?'id-ce-policyConstraints',
- critical = true} | Rest], ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions, Verify,
- AccErr0) ->
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-policyConstraints',
+ critical = true} = Ext | Rest], ValidationState,
+ ExistBasicCon, SelfSigned, UserState0, VerifyFun) ->
%% TODO: Remove this clause when policy handling is
%% fully implemented
- AccErr =
- not_valid({bad_cert, unknown_critical_extension}, Verify, AccErr0),
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-validate_extensions([#'Extension'{extnID = ?'id-ce-policyConstraints',
- extnValue = #'PolicyConstraints'{
- requireExplicitPolicy = ExpPolicy,
- inhibitPolicyMapping = MapPolicy}}
- | Rest], ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr) ->
+ UserState = verify_fun(OtpCert, {extension, Ext},
+ UserState0, VerifyFun),
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-policyConstraints',
+ extnValue = #'PolicyConstraints'{
+ requireExplicitPolicy = ExpPolicy,
+ inhibitPolicyMapping = MapPolicy}}
+ | Rest], ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun) ->
%% TODO: Policy imp incomplete
- NewValidationState = add_policy_constraints(ExpPolicy, MapPolicy,
+ NewValidationState = add_policy_constraints(ExpPolicy, MapPolicy,
ValidationState),
- validate_extensions(Rest, NewValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
+ validate_extensions(OtpCert, Rest, NewValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
-validate_extensions([Extension | Rest], ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr) ->
- validate_extensions(Rest, ValidationState, ExistBasicCon, SelfSigned,
- [Extension | UnknownExtensions], Verify, AccErr).
+validate_extensions(OtpCert, [#'Extension'{} = Extension | Rest],
+ ValidationState, ExistBasicCon,
+ SelfSigned, UserState0, VerifyFun) ->
+ UserState = verify_fun(OtpCert, {extension, Extension}, UserState0, VerifyFun),
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon, SelfSigned,
+ UserState, VerifyFun).
is_valid_key_usage(KeyUse, Use) ->
lists:member(Use, KeyUse).
validate_subject_alt_names([]) ->
- true;
+ false;
validate_subject_alt_names([AltName | Rest]) ->
case is_valid_subject_alt_name(AltName) of
true ->
- validate_subject_alt_names(Rest);
+ true;
false ->
- false
+ validate_subject_alt_names(Rest)
end.
is_valid_subject_alt_name({Name, Value}) when Name == rfc822Name;
@@ -607,14 +677,11 @@ is_valid_subject_alt_name({directoryName, _}) ->
true;
is_valid_subject_alt_name({_, [_|_]}) ->
true;
+is_valid_subject_alt_name({otherName, #'AnotherName'{}}) ->
+ false;
is_valid_subject_alt_name({_, _}) ->
false.
-min(N, M) when N =< M ->
- N;
-min(_, M) ->
- M.
-
is_ip_address(Address) ->
case inet_parse:address(Address) of
{ok, _} ->
@@ -677,10 +744,11 @@ split_auth_path(URIPart) ->
end.
split_uri(UriPart, SplitChar, NoMatchResult, SkipLeft, SkipRight) ->
- case regexp:first_match(UriPart, SplitChar) of
- {match, Match, _} ->
- {string:substr(UriPart, 1, Match - SkipLeft),
- string:substr(UriPart, Match + SkipRight, length(UriPart))};
+ case re:run(UriPart, SplitChar) of
+ {match,[{Start, _}]} ->
+ StrPos = Start + 1,
+ {string:substr(UriPart, 1, StrPos - SkipLeft),
+ string:substr(UriPart, StrPos + SkipRight, length(UriPart))};
nomatch ->
NoMatchResult
end.
@@ -933,7 +1001,7 @@ add_policy_constraints(ExpPolicy, MapPolicy,
policy_constraint(Current, asn1_NOVALUE, _) ->
Current;
policy_constraint(Current, New, CertNum) ->
- min(Current, New + CertNum).
+ erlang:min(Current, New + CertNum).
process_policy_tree(_,_, ?NULL) ->
?NULL;
diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl
index c7d4080adb..b86d7a1f0c 100644
--- a/lib/public_key/src/pubkey_cert_records.erl
+++ b/lib/public_key/src/pubkey_cert_records.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
@@ -23,96 +23,81 @@
-include("public_key.hrl").
--export([decode_cert/2, encode_cert/1, encode_tbs_cert/1, transform/2]).
-
--export([old_decode_cert/2, old_encode_cert/1]). %% Debugging and testing new code.
+-export([decode_cert/1, transform/2, supportedPublicKeyAlgorithms/1]).
%%====================================================================
%% Internal application API
%%====================================================================
-decode_cert(DerCert, plain) ->
- 'OTP-PUB-KEY':decode('Certificate', DerCert);
-decode_cert(DerCert, otp) ->
+%%--------------------------------------------------------------------
+-spec decode_cert(DerCert::binary()) -> {ok, #'OTPCertificate'{}}.
+%%
+%% Description: Recursively decodes a Certificate.
+%%--------------------------------------------------------------------
+decode_cert(DerCert) ->
{ok, Cert} = 'OTP-PUB-KEY':decode('OTPCertificate', DerCert),
- {ok, decode_all_otp(Cert)}.
-
-old_decode_cert(DerCert, otp) ->
- {ok, Cert} = 'OTP-PUB-KEY':decode('Certificate', DerCert),
- {ok, plain_to_otp(Cert)}.
-
-old_encode_cert(Cert) ->
- PlainCert = otp_to_plain(Cert),
- {ok, EncCert} = 'OTP-PUB-KEY':encode('Certificate', PlainCert),
- list_to_binary(EncCert).
-
-
-encode_cert(Cert = #'Certificate'{}) ->
- {ok, EncCert} = 'OTP-PUB-KEY':encode('Certificate', Cert),
- list_to_binary(EncCert);
-encode_cert(C = #'OTPCertificate'{tbsCertificate = TBS =
- #'OTPTBSCertificate'{
- issuer=Issuer0,
- subject=Subject0,
- subjectPublicKeyInfo=Spki0,
- extensions=Exts0}
- }) ->
- Issuer = transform(Issuer0,encode),
- Subject = transform(Subject0,encode),
- Spki = encode_supportedPublicKey(Spki0),
- Exts = encode_extensions(Exts0),
- %% io:format("Extensions ~p~n",[Exts]),
- Cert = C#'OTPCertificate'{tbsCertificate=
- TBS#'OTPTBSCertificate'{
- issuer=Issuer, subject=Subject,
- subjectPublicKeyInfo=Spki,
- extensions=Exts}},
- {ok, EncCert} = 'OTP-PUB-KEY':encode('OTPCertificate', Cert),
- list_to_binary(EncCert).
+ #'OTPCertificate'{tbsCertificate = TBS} = Cert,
+ {ok, Cert#'OTPCertificate'{tbsCertificate = decode_tbs(TBS)}}.
-encode_tbs_cert(TBS = #'OTPTBSCertificate'{
- issuer=Issuer0,
- subject=Subject0,
- subjectPublicKeyInfo=Spki0,
- extensions=Exts0}) ->
- Issuer = transform(Issuer0,encode),
- Subject = transform(Subject0,encode),
- Spki = encode_supportedPublicKey(Spki0),
- Exts = encode_extensions(Exts0),
- TBSCert = TBS#'OTPTBSCertificate'{issuer=Issuer,subject=Subject,
- subjectPublicKeyInfo=Spki,extensions=Exts},
- {ok, EncTBSCert} = 'OTP-PUB-KEY':encode('OTPTBSCertificate', TBSCert),
- list_to_binary(EncTBSCert).
+%%--------------------------------------------------------------------
+-spec transform(term(), encode | decode) ->term().
+%%
+%% Description: Transforms between encoded and decode otp formated
+%% certificate parts.
+%%--------------------------------------------------------------------
+
+transform(#'OTPCertificate'{tbsCertificate = TBS} = Cert, encode) ->
+ Cert#'OTPCertificate'{tbsCertificate=encode_tbs(TBS)};
+transform(#'OTPCertificate'{tbsCertificate = TBS} = Cert, decode) ->
+ Cert#'OTPCertificate'{tbsCertificate=decode_tbs(TBS)};
+transform(#'OTPTBSCertificate'{}= TBS, encode) ->
+ encode_tbs(TBS);
+transform(#'OTPTBSCertificate'{}= TBS, decode) ->
+ decode_tbs(TBS);
+transform(#'AttributeTypeAndValue'{type=Id,value=Value0} = ATAV, Func) ->
+ {ok, Value} =
+ case attribute_type(Id) of
+ Type when is_atom(Type) -> 'OTP-PUB-KEY':Func(Type, Value0);
+ _UnknownType -> {ok, Value0}
+ end,
+ ATAV#'AttributeTypeAndValue'{value=Value};
+transform(AKI = #'AuthorityKeyIdentifier'{authorityCertIssuer=ACI},Func) ->
+ AKI#'AuthorityKeyIdentifier'{authorityCertIssuer=transform(ACI,Func)};
+transform(List = [{directoryName, _}],Func) ->
+ [{directoryName, transform(Value,Func)} || {directoryName, Value} <- List];
+transform({directoryName, Value},Func) ->
+ {directoryName, transform(Value,Func)};
+transform({rdnSequence, SeqList},Func) when is_list(SeqList) ->
+ {rdnSequence,
+ lists:map(fun(Seq) ->
+ lists:map(fun(Element) -> transform(Element,Func) end, Seq)
+ end, SeqList)};
+transform(#'NameConstraints'{permittedSubtrees=Permitted, excludedSubtrees=Excluded}, Func) ->
+ #'NameConstraints'{permittedSubtrees=transform_sub_tree(Permitted,Func),
+ excludedSubtrees=transform_sub_tree(Excluded,Func)};
+
+transform(Other,_) ->
+ Other.
%%--------------------------------------------------------------------
-%%% Internal functions
+-spec supportedPublicKeyAlgorithms(Oid::tuple()) -> asn1_type().
+%%
+%% Description: Returns the public key type for an algorithm
+%% identifier tuple as found in SubjectPublicKeyInfo.
+%%
%%--------------------------------------------------------------------
-
-decode_all_otp(C = #'OTPCertificate'{tbsCertificate = TBS =
- #'OTPTBSCertificate'{
- issuer=Issuer0,
- subject=Subject0,
- subjectPublicKeyInfo=Spki0,
- extensions=Exts0}
- }) ->
- Issuer = transform(Issuer0,decode),
- Subject = transform(Subject0,decode),
- Spki = decode_supportedPublicKey(Spki0),
- Exts = decode_extensions(Exts0),
- %% io:format("Extensions ~p~n",[Exts]),
- C#'OTPCertificate'{tbsCertificate=
- TBS#'OTPTBSCertificate'{
- issuer=Issuer, subject=Subject,
- subjectPublicKeyInfo=Spki,extensions=Exts}}.
-
-
-%%% SubjectPublicKey
supportedPublicKeyAlgorithms(?'rsaEncryption') -> 'RSAPublicKey';
supportedPublicKeyAlgorithms(?'id-dsa') -> 'DSAPublicKey';
supportedPublicKeyAlgorithms(?'dhpublicnumber') -> 'DHPublicKey';
supportedPublicKeyAlgorithms(?'id-keyExchangeAlgorithm') -> 'KEA-PublicKey';
supportedPublicKeyAlgorithms(?'id-ecPublicKey') -> 'ECPoint'.
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+%%% SubjectPublicKey
+
decode_supportedPublicKey(#'OTPSubjectPublicKeyInfo'{algorithm= PA =
#'PublicKeyAlgorithm'{algorithm=Algo},
subjectPublicKey = {0,SPK0}}) ->
@@ -186,33 +171,28 @@ encode_extensions(Exts) ->
end
end, Exts).
-transform(#'AttributeTypeAndValue'{type=Id,value=Value0} = ATAV, Func) ->
- {ok, Value} =
- case attribute_type(Id) of
- Type when is_atom(Type) -> 'OTP-PUB-KEY':Func(Type, Value0);
- _UnknownType -> {ok, Value0}
- end,
- ATAV#'AttributeTypeAndValue'{value=Value};
-transform(AKI = #'AuthorityKeyIdentifier'{authorityCertIssuer=ACI},Func) ->
- AKI#'AuthorityKeyIdentifier'{authorityCertIssuer=transform(ACI,Func)};
-transform(List = [{directoryName, _}],Func) ->
- [{directoryName, transform(Value,Func)} || {directoryName, Value} <- List];
-transform({directoryName, Value},Func) ->
- {directoryName, transform(Value,Func)};
-transform({rdnSequence, SeqList},Func) when is_list(SeqList) ->
- {rdnSequence,
- lists:map(fun(Seq) ->
- lists:map(fun(Element) -> transform(Element,Func) end, Seq)
- end, SeqList)};
-%% transform(List = [{rdnSequence, _}|_],Func) ->
-%% lists:map(fun(Element) -> transform(Element,Func) end, List);
-transform(#'NameConstraints'{permittedSubtrees=Permitted, excludedSubtrees=Excluded}, Func) ->
- Res = #'NameConstraints'{permittedSubtrees=transform_sub_tree(Permitted,Func),
- excludedSubtrees=transform_sub_tree(Excluded,Func)},
-%% io:format("~p~n",[Res]),
- Res;
-transform(Other,_) ->
- Other.
+encode_tbs(TBS=#'OTPTBSCertificate'{issuer=Issuer0,
+ subject=Subject0,
+ subjectPublicKeyInfo=Spki0,
+ extensions=Exts0}) ->
+ Issuer = transform(Issuer0,encode),
+ Subject = transform(Subject0,encode),
+ Spki = encode_supportedPublicKey(Spki0),
+ Exts = encode_extensions(Exts0),
+ TBS#'OTPTBSCertificate'{issuer=Issuer, subject=Subject,
+ subjectPublicKeyInfo=Spki,extensions=Exts}.
+
+decode_tbs(TBS = #'OTPTBSCertificate'{issuer=Issuer0,
+ subject=Subject0,
+ subjectPublicKeyInfo=Spki0,
+ extensions=Exts0}) ->
+ Issuer = transform(Issuer0,decode),
+ Subject = transform(Subject0,decode),
+ Spki = decode_supportedPublicKey(Spki0),
+ Exts = decode_extensions(Exts0),
+ TBS#'OTPTBSCertificate'{issuer=Issuer, subject=Subject,
+ subjectPublicKeyInfo=Spki,extensions=Exts}.
+
transform_sub_tree(asn1_NOVALUE,_) -> asn1_NOVALUE;
transform_sub_tree(TreeList,Func) ->
[Tree#'GeneralSubtree'{base=transform(Name,Func)} ||
@@ -236,303 +216,3 @@ attribute_type(?'id-at-pseudonym') -> 'X520Pseudonym';
attribute_type(?'id-domainComponent') -> 'DomainComponent';
attribute_type(?'id-emailAddress') -> 'EmailAddress';
attribute_type(Type) -> Type.
-
-%%% Old code transforms
-
-plain_to_otp(#'Certificate'{tbsCertificate = TBSCert,
- signatureAlgorithm = SigAlg,
- signature = Signature} = Cert) ->
- Cert#'Certificate'{tbsCertificate = plain_to_otp(TBSCert),
- signatureAlgorithm = plain_to_otp(SigAlg),
- signature = plain_to_otp(Signature)};
-
-plain_to_otp(#'TBSCertificate'{signature = Signature,
- issuer = Issuer,
- subject = Subject,
- subjectPublicKeyInfo = SPubKeyInfo,
- extensions = Extensions} = TBSCert) ->
-
- TBSCert#'TBSCertificate'{signature = plain_to_otp(Signature),
- issuer = plain_to_otp(Issuer),
- subject =
- plain_to_otp(Subject),
- subjectPublicKeyInfo =
- plain_to_otp(SPubKeyInfo),
- extensions =
- plain_to_otp_extensions(Extensions)
- };
-
-plain_to_otp(#'AlgorithmIdentifier'{algorithm = Algorithm,
- parameters = Params}) ->
- SignAlgAny =
- #'SignatureAlgorithm-Any'{algorithm = Algorithm,
- parameters = Params},
- {ok, AnyEnc} = 'OTP-PUB-KEY':encode('SignatureAlgorithm-Any',
- SignAlgAny),
- {ok, SignAlg} = 'OTP-PUB-KEY':decode('SignatureAlgorithm',
- list_to_binary(AnyEnc)),
- SignAlg;
-
-plain_to_otp({rdnSequence, SeqList}) when is_list(SeqList) ->
- {rdnSequence,
- lists:map(fun(Seq) ->
- lists:map(fun(Element) ->
- plain_to_otp(Element)
- end,
- Seq)
- end, SeqList)};
-
-plain_to_otp(#'AttributeTypeAndValue'{} = ATAV) ->
- {ok, ATAVEnc} =
- 'OTP-PUB-KEY':encode('AttributeTypeAndValue', ATAV),
- {ok, ATAVDec} = 'OTP-PUB-KEY':decode('OTPAttributeTypeAndValue',
- list_to_binary(ATAVEnc)),
- #'AttributeTypeAndValue'{type = ATAVDec#'OTPAttributeTypeAndValue'.type,
- value =
- ATAVDec#'OTPAttributeTypeAndValue'.value};
-
-plain_to_otp(#'SubjectPublicKeyInfo'{algorithm =
- #'AlgorithmIdentifier'{algorithm
- = Algo,
- parameters =
- Params},
- subjectPublicKey = PublicKey}) ->
-
- AnyAlgo = #'PublicKeyAlgorithm'{algorithm = Algo,
- parameters = Params},
- {0, AnyKey} = PublicKey,
- AnyDec = #'OTPSubjectPublicKeyInfo-Any'{algorithm = AnyAlgo,
- subjectPublicKey = AnyKey},
- {ok, AnyEnc} =
- 'OTP-PUB-KEY':encode('OTPSubjectPublicKeyInfo-Any', AnyDec),
- {ok, InfoDec} = 'OTP-PUB-KEY':decode('OTPOLDSubjectPublicKeyInfo',
- list_to_binary(AnyEnc)),
-
- AlgorithmDec = InfoDec#'OTPOLDSubjectPublicKeyInfo'.algorithm,
- AlgoDec = AlgorithmDec#'OTPOLDSubjectPublicKeyInfo_algorithm'.algo,
- NewParams = AlgorithmDec#'OTPOLDSubjectPublicKeyInfo_algorithm'.parameters,
- PublicKeyDec = InfoDec#'OTPOLDSubjectPublicKeyInfo'.subjectPublicKey,
- NewAlgorithmDec =
- #'SubjectPublicKeyInfoAlgorithm'{algorithm = AlgoDec,
- parameters = NewParams},
- #'SubjectPublicKeyInfo'{algorithm = NewAlgorithmDec,
- subjectPublicKey = PublicKeyDec
- };
-
-plain_to_otp(#'Extension'{extnID = ExtID,
- critical = Critical,
- extnValue = Value})
- when ExtID == ?'id-ce-authorityKeyIdentifier';
- ExtID == ?'id-ce-subjectKeyIdentifier';
- ExtID == ?'id-ce-keyUsage';
- ExtID == ?'id-ce-privateKeyUsagePeriod';
- ExtID == ?'id-ce-certificatePolicies';
- ExtID == ?'id-ce-policyMappings';
- ExtID == ?'id-ce-subjectAltName';
- ExtID == ?'id-ce-issuerAltName';
- ExtID == ?'id-ce-subjectDirectoryAttributes';
- ExtID == ?'id-ce-basicConstraints';
- ExtID == ?'id-ce-nameConstraints';
- ExtID == ?'id-ce-policyConstraints';
- ExtID == ?'id-ce-extKeyUsage';
- ExtID == ?'id-ce-cRLDistributionPoints';
- ExtID == ?'id-ce-inhibitAnyPolicy';
- ExtID == ?'id-ce-freshestCRL' ->
- ExtAny = #'Extension-Any'{extnID = ExtID,
- critical = Critical,
- extnValue = Value},
- {ok, AnyEnc} = 'OTP-PUB-KEY':encode('Extension-Any', ExtAny),
- {ok, ExtDec} = 'OTP-PUB-KEY':decode('OTPExtension',
- list_to_binary(AnyEnc)),
-
- ExtValue = plain_to_otp_extension_value(ExtID,
- ExtDec#'OTPExtension'.extnValue),
- #'Extension'{extnID = ExtID,
- critical = ExtDec#'OTPExtension'.critical,
- extnValue = ExtValue};
-
-plain_to_otp(#'Extension'{} = Ext) ->
- Ext;
-
-plain_to_otp(#'AuthorityKeyIdentifier'{} = Ext) ->
- CertIssuer = Ext#'AuthorityKeyIdentifier'.authorityCertIssuer,
- Ext#'AuthorityKeyIdentifier'{authorityCertIssuer =
- plain_to_otp(CertIssuer)};
-
-
-plain_to_otp([{directoryName, Value}]) ->
- [{directoryName, plain_to_otp(Value)}];
-
-plain_to_otp(Value) ->
- Value.
-
-otp_to_plain(#'Certificate'{tbsCertificate = TBSCert,
- signatureAlgorithm = SigAlg,
- signature = Signature} = Cert) ->
- Cert#'Certificate'{tbsCertificate = otp_to_plain(TBSCert),
- signatureAlgorithm =
- otp_to_plain(SigAlg),
- signature = otp_to_plain(Signature)};
-
-otp_to_plain(#'TBSCertificate'{signature = Signature,
- issuer = Issuer,
- subject = Subject,
- subjectPublicKeyInfo = SPubKeyInfo,
- extensions = Extensions} = TBSCert) ->
-
- TBSCert#'TBSCertificate'{signature = otp_to_plain(Signature),
- issuer = otp_to_plain(Issuer),
- subject =
- otp_to_plain(Subject),
- subjectPublicKeyInfo =
- otp_to_plain(SPubKeyInfo),
- extensions = otp_to_plain_extensions(Extensions)
- };
-
-otp_to_plain(#'SignatureAlgorithm'{} = SignAlg) ->
- {ok, EncSignAlg} = 'OTP-PUB-KEY':encode('SignatureAlgorithm', SignAlg),
- {ok, #'SignatureAlgorithm-Any'{algorithm = Algorithm,
- parameters = Params}} =
- 'OTP-PUB-KEY':decode('SignatureAlgorithm-Any',
- list_to_binary(EncSignAlg)),
- #'AlgorithmIdentifier'{algorithm = Algorithm,
- parameters = Params};
-
-otp_to_plain({rdnSequence, SeqList}) when is_list(SeqList) ->
- {rdnSequence,
- lists:map(fun(Seq) ->
- lists:map(fun(Element) ->
- otp_to_plain(Element)
- end,
- Seq)
- end, SeqList)};
-
-otp_to_plain(#'AttributeTypeAndValue'{type = Type, value = Value}) ->
- {ok, ATAVEnc} =
- 'OTP-PUB-KEY':encode('OTPAttributeTypeAndValue',
- #'OTPAttributeTypeAndValue'{type = Type,
- value = Value}),
- {ok, ATAVDec} = 'OTP-PUB-KEY':decode('AttributeTypeAndValue',
- list_to_binary(ATAVEnc)),
- ATAVDec;
-
-otp_to_plain(#'SubjectPublicKeyInfo'{algorithm =
- #'SubjectPublicKeyInfoAlgorithm'{
- algorithm = Algo,
- parameters =
- Params},
- subjectPublicKey = PublicKey}) ->
-
- OtpAlgo = #'OTPOLDSubjectPublicKeyInfo_algorithm'{algo = Algo,
- parameters = Params},
- OtpDec = #'OTPOLDSubjectPublicKeyInfo'{algorithm = OtpAlgo,
- subjectPublicKey = PublicKey},
- {ok, OtpEnc} =
- 'OTP-PUB-KEY':encode('OTPOLDSubjectPublicKeyInfo', OtpDec),
-
- {ok, AnyDec} = 'OTP-PUB-KEY':decode('OTPSubjectPublicKeyInfo-Any',
- list_to_binary(OtpEnc)),
-
- #'OTPSubjectPublicKeyInfo-Any'{algorithm = #'PublicKeyAlgorithm'{
- algorithm = NewAlgo,
- parameters = NewParams},
- subjectPublicKey = Bin} = AnyDec,
-
- #'SubjectPublicKeyInfo'{algorithm =
- #'AlgorithmIdentifier'{
- algorithm = NewAlgo,
- parameters = plain_key_params(NewParams)},
- subjectPublicKey =
- {0, Bin}
- };
-
-otp_to_plain(#'Extension'{extnID = ExtID,
- extnValue = Value} = Ext) ->
- ExtValue =
- otp_to_plain_extension_value(ExtID, Value),
-
- Ext#'Extension'{extnValue = ExtValue};
-
-otp_to_plain(#'AuthorityKeyIdentifier'{} = Ext) ->
- CertIssuer = Ext#'AuthorityKeyIdentifier'.authorityCertIssuer,
- Ext#'AuthorityKeyIdentifier'{authorityCertIssuer =
- otp_to_plain(CertIssuer)};
-
-otp_to_plain([{directoryName, Value}]) ->
- [{directoryName, otp_to_plain(Value)}];
-
-otp_to_plain(Value) ->
- Value.
-
-plain_key_params('NULL') ->
- <<5,0>>;
-plain_key_params(Value) ->
- Value.
-
-plain_to_otp_extension_value(?'id-ce-authorityKeyIdentifier', Value) ->
- plain_to_otp(Value);
-plain_to_otp_extension_value(_, Value) ->
- Value.
-
-plain_to_otp_extensions(Exts) when is_list(Exts) ->
- lists:map(fun(Ext) -> plain_to_otp(Ext) end, Exts).
-
-otp_to_plain_extension_value(?'id-ce-authorityKeyIdentifier', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('AuthorityKeyIdentifier',
- otp_to_plain(Value)),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-subjectKeyIdentifier', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectKeyIdentifier', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-keyUsage', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('KeyUsage', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-privateKeyUsagePeriod', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('PrivateKeyUsagePeriod', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-certificatePolicies', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('CertificatePolicies', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-policyMappings', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('PolicyMappings', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-subjectAltName', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectAltName', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-issuerAltName', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('IssuerAltName', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-subjectDirectoryAttributes', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectDirectoryAttributes', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-basicConstraints', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('BasicConstraints', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-nameConstraints', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('NameConstraints', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-policyConstraints', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('PolicyConstraints', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-extKeyUsage', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('ExtKeyUsage', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-cRLDistributionPoints', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('CRLDistributionPoints', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-inhibitAnyPolicy', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('InhibitAnyPolicy', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-freshestCRL', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('FreshestCRL', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(_Id, Value) ->
- Value.
-
-otp_to_plain_extension_value_format(Value) ->
- list_to_binary(Value).
-
-otp_to_plain_extensions(Exts) when is_list(Exts) ->
- lists:map(fun(Ext) ->
- otp_to_plain(Ext)
- end, Exts).
diff --git a/lib/public_key/src/pubkey_crypto.erl b/lib/public_key/src/pubkey_crypto.erl
deleted file mode 100644
index 4ab655e977..0000000000
--- a/lib/public_key/src/pubkey_crypto.erl
+++ /dev/null
@@ -1,160 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2010. 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%
-%%
-
-%%
-%% Description: Functions that call the crypto driver.
-
--module(pubkey_crypto).
-
--include("public_key.hrl").
-
--export([encrypt_public/3, decrypt_private/3,
- encrypt_private/3, decrypt_public/3,
- sign/2, sign/3, verify/5, gen_key/2]).
-
--define(UINT32(X), X:32/unsigned-big-integer).
-
-%%====================================================================
-%% Internal application API
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: encrypt(PlainText, Key, Padding) -> Encrypted
-%%
-%% PlainText = binary()
-%% Key = rsa_public_key() | rsa_private_key()
-%% Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding
-%% Encrypted = binary()
-%%
-%% Description: Public key encrypts PlainText.
-%%--------------------------------------------------------------------
-encrypt_public(PlainText, #'RSAPublicKey'{modulus=N,publicExponent=E},
- Padding) ->
- crypto:rsa_public_encrypt(PlainText, [crypto:mpint(E),crypto:mpint(N)],
- Padding);
-encrypt_public(PlainText, #'RSAPrivateKey'{modulus=N,publicExponent=E},
- Padding) ->
- crypto:rsa_public_encrypt(PlainText, [crypto:mpint(E),crypto:mpint(N)],
- Padding).
-
-encrypt_private(PlainText, #'RSAPrivateKey'{modulus = N,
- publicExponent = E,
- privateExponent = D}, Padding) ->
- crypto:rsa_private_encrypt(PlainText, [crypto:mpint(E),
- crypto:mpint(N),
- crypto:mpint(D)], Padding).
-
-%%--------------------------------------------------------------------
-%% Function: decrypt(CipherText, Key) -> PlainText
-%%
-%% ChipherText = binary()
-%% Key = rsa_private_key()
-%% Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding
-%% PlainText = binary()
-%%
-%% Description: Uses private key to decrypt public key encrypted data.
-%%--------------------------------------------------------------------
-decrypt_private(CipherText,
- #'RSAPrivateKey'{modulus = N,publicExponent = E,
- privateExponent = D},
- Padding) ->
- crypto:rsa_private_decrypt(CipherText,
- [crypto:mpint(E), crypto:mpint(N),
- crypto:mpint(D)], Padding).
-decrypt_public(CipherText, #'RSAPublicKey'{modulus = N, publicExponent = E},
- Padding) ->
- crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)],
- Padding);
-decrypt_public(CipherText, #'RSAPrivateKey'{modulus = N, publicExponent = E},
- Padding) ->
- crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)],
- Padding).
-
-%%--------------------------------------------------------------------
-%% Function: sign(PlainText, Key) ->
-%% sign(DigestType, PlainText, Key) -> Signature
-%%
-%% DigestType = sha | md5
-%% PlainText = binary()
-%% Key = rsa_private_key() | dsa_private_key()
-%% Signature = binary()
-%%
-%% Description: Signs PlainText using Key.
-%%--------------------------------------------------------------------
-sign(PlainText, Digest) ->
- sign(sha, PlainText, Digest).
-
-sign(DigestType, PlainText, #'RSAPrivateKey'{modulus = N, publicExponent = E,
- privateExponent = D}) ->
- crypto:rsa_sign(DigestType, sized_binary(PlainText), [crypto:mpint(E),
- crypto:mpint(N),
- crypto:mpint(D)]);
-
-sign(sha, PlainText, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) ->
- crypto:dss_sign(sized_binary(PlainText),
- [crypto:mpint(P), crypto:mpint(Q),
- crypto:mpint(G), crypto:mpint(X)]).
-
-%%--------------------------------------------------------------------
-%% Function: verify(DigestType, PlainText, Signature, Key) -> true | false
-%%
-%% DigestType = sha | md5
-%% PlainText = binary()
-%% Signature = binary()
-%% Key = rsa_public_key() | dsa_public_key()
-%%
-%% Description: Verifies the signature <Signature>.
-%%--------------------------------------------------------------------
-verify(DigestType, PlainText, Signature,
- #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}, _) ->
- crypto:rsa_verify(DigestType,
- sized_binary(PlainText),
- sized_binary(Signature),
- [crypto:mpint(Exp), crypto:mpint(Mod)]);
-
-verify(sha, PlainText, Signature, Key, #'Dss-Parms'{p = P, q = Q, g = G}) ->
- crypto:dss_verify(sized_binary(PlainText),
- sized_binary(Signature),
- [crypto:mpint(P), crypto:mpint(Q),
- crypto:mpint(G), crypto:mpint(Key)]).
-
-
-%%--------------------------------------------------------------------
-%% Function: gen_key(Type, Params) ->
-%% Type = diffie_hellman
-%% Params = [P,G] | [Y, P, G]
-%% Description: Generates keys.
-%% -----------------------------------------------------------------
-gen_key(diffie_hellman, [Y, P, G]) ->
- crypto:dh_generate_key(crypto:mpint(Y), [crypto:mpint(P),
- crypto:mpint(G)]);
-gen_key(diffie_hellman, [P, G]) ->
- crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]).
-
-%%% TODO: Support rsa, dss key_gen
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-sized_binary(Binary) when is_binary(Binary) ->
- Size = size(Binary),
- <<?UINT32(Size), Binary/binary>>;
-sized_binary(List) ->
- sized_binary(list_to_binary(List)).
-
diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl
index 9fc17b6f73..c26815bc04 100644
--- a/lib/public_key/src/pubkey_pem.erl
+++ b/lib/public_key/src/pubkey_pem.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
@@ -40,7 +40,10 @@
-module(pubkey_pem).
--export([read_file/1, read_file/2, write_file/2, decode/2]).
+-include("public_key.hrl").
+
+-export([encode/1, decode/1, decipher/2, cipher/3]).
+%% Backwards compatibility
-export([decode_key/2]).
-define(ENCODED_LINE_LENGTH, 64).
@@ -48,28 +51,84 @@
%%====================================================================
%% Internal application API
%%====================================================================
-read_file(File) ->
- read_file(File, no_passwd).
-read_file(File, Passwd) ->
- {ok, Bin} = file:read_file(File),
- decode(Bin, Passwd).
+%%--------------------------------------------------------------------
+-spec decode(binary()) -> [pem_entry()].
+%%
+%% Description: Decodes a PEM binary.
+%%--------------------------------------------------------------------
+decode(Bin) ->
+ decode_pem_entries(split_bin(Bin), []).
-write_file(File, Ds) ->
- file:write_file(File, encode_file(Ds)).
+%%--------------------------------------------------------------------
+-spec encode([pem_entry()]) -> iolist().
+%%
+%% Description: Encodes a list of PEM entries.
+%%--------------------------------------------------------------------
+encode(PemEntries) ->
+ encode_pem_entries(PemEntries).
-decode_key({_Type, Bin, not_encrypted}, _) ->
- Bin;
-decode_key({_Type, Bin, {Chipher,Salt}}, Password) ->
- decode_key(Bin, Password, Chipher, Salt).
+%%--------------------------------------------------------------------
+-spec decipher({pki_asn1_type(), DerEncrypted::binary(),{Cipher :: string(),
+ Salt :: binary()}},
+ string()) -> Der::binary().
+%%
+%% Description: Deciphers a decrypted pem entry.
+%%--------------------------------------------------------------------
+decipher({_, DecryptDer, {Cipher,Salt}}, Password) ->
+ decode_key(DecryptDer, Password, Cipher, Salt).
-decode(Bin, Passwd) ->
- decode_file(split_bin(Bin), Passwd).
+%%--------------------------------------------------------------------
+-spec cipher(Der::binary(),{Cipher :: string(), Salt :: binary()} ,
+ string()) -> binary().
+%%
+%% Description: Ciphers a PEM entry
+%%--------------------------------------------------------------------
+cipher(Der, {Cipher,Salt}, Password)->
+ encode_key(Der, Password, Cipher, Salt).
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+encode_pem_entries(Entries) ->
+ [encode_pem_entry(Entry) || Entry <- Entries].
+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({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"].
+
+decode_pem_entries([], Entries) ->
+ lists:reverse(Entries);
+decode_pem_entries([<<>>], Entries) ->
+ lists:reverse(Entries);
+decode_pem_entries([<<>> | Lines], Entries) ->
+ decode_pem_entries(Lines, Entries);
+decode_pem_entries([Start| Lines], Entries) ->
+ case pem_end(Start) of
+ undefined ->
+ decode_pem_entries(Lines, Entries);
+ _End ->
+ {Entry, RestLines} = join_entry(Lines, []),
+ decode_pem_entries(RestLines, [decode_pem_entry(Start, Entry) | Entries])
+ end.
+
+decode_pem_entry(Start, [<<"Proc-Type: 4,ENCRYPTED", _/binary>>, Line | Lines]) ->
+ 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, ","),
+ {Type, Decoded, {Cipher, unhex(Salt)}};
+decode_pem_entry(Start, Lines) ->
+ Type = asn1_type(Start),
+ Cs = erlang:iolist_to_binary(Lines),
+ Decoded = base64:mime_decode(Cs),
+ {Type, Decoded, not_encrypted}.
+
split_bin(Bin) ->
split_bin(0, Bin).
@@ -85,82 +144,22 @@ split_bin(N, Bin) ->
split_bin(N+1, Bin)
end.
-decode_file(Bin, Passwd) ->
- decode_file(Bin, [], [Passwd]).
-
-decode_file([<<"-----BEGIN CERTIFICATE REQUEST-----", _/binary>>|Rest], Ens, Info) ->
- decode_file2(Rest, [], Ens, cert_req, Info);
-decode_file([<<"-----BEGIN CERTIFICATE-----", _/binary>>|Rest], Ens, Info) ->
- decode_file2(Rest, [], Ens, cert, Info);
-decode_file([<<"-----BEGIN RSA PRIVATE KEY-----", _/binary>>|Rest], Ens, Info) ->
- decode_file2(Rest, [], Ens, rsa_private_key, Info);
-decode_file([<<"-----BEGIN DSA PRIVATE KEY-----", _/binary>>|Rest], Ens, Info) ->
- decode_file2(Rest, [], Ens, dsa_private_key, Info);
-decode_file([<<"-----BEGIN DH PARAMETERS-----", _/binary>>|Rest], Ens, Info) ->
- decode_file2(Rest, [], Ens, dh_params, Info);
-decode_file([_|Rest], Ens, Info) ->
- decode_file(Rest, Ens, Info);
-decode_file([], Ens, _Info) ->
- {ok, lists:reverse(Ens)}.
-
-decode_file2([<<"Proc-Type: 4,ENCRYPTED", _/binary>>| Rest0], RLs, Ens, Tag, Info0) ->
- [InfoLine|Rest] = Rest0,
- Info = dek_info(InfoLine, Info0),
- decode_file2(Rest, RLs, Ens, Tag, Info);
-decode_file2([<<"-----END", _/binary>>| Rest], RLs, Ens, Tag, Info0) ->
- Cs = erlang:iolist_to_binary(lists:reverse(RLs)),
- Bin = base64:mime_decode(Cs),
- case Info0 of
- [Password, Cipher, SaltHex | Info1] ->
- Salt = unhex(SaltHex),
- Enc = {Cipher, Salt},
- Decoded = decode_key(Bin, Password, Cipher, Salt),
- decode_file(Rest, [{Tag, Decoded, Enc}| Ens], Info1);
- _ ->
- decode_file(Rest, [{Tag, Bin, not_encrypted}| Ens], Info0)
- end;
-decode_file2([L|Rest], RLs, Ens, Tag, Info0) ->
- decode_file2(Rest, [L|RLs], Ens, Tag, Info0);
-decode_file2([], _, Ens, _, _) ->
- {ok, lists:reverse(Ens)}.
-
-%% TODO Support same as decode_file
-encode_file(Ds) ->
- lists:map(
- fun({cert, Bin}) ->
- %% PKIX (X.509)
- ["-----BEGIN CERTIFICATE-----\n",
- b64encode_and_split(Bin),
- "-----END CERTIFICATE-----\n\n"];
- ({cert_req, Bin}) ->
- %% PKCS#10
- ["-----BEGIN CERTIFICATE REQUEST-----\n",
- b64encode_and_split(Bin),
- "-----END CERTIFICATE REQUEST-----\n\n"];
- ({rsa_private_key, Bin}) ->
- %% PKCS#?
- ["XXX Following key assumed not encrypted\n",
- "-----BEGIN RSA PRIVATE KEY-----\n",
- b64encode_and_split(Bin),
- "-----END RSA PRIVATE KEY-----\n\n"]
- end, Ds).
-
-dek_info(Line0, Info) ->
- Line = binary_to_list(Line0),
- [_, DekInfo0] = string:tokens(Line, ": "),
- DekInfo1 = string:tokens(DekInfo0, ",\n"),
- Info ++ DekInfo1.
+b64encode_and_split(Bin) ->
+ split_lines(base64:encode(Bin)).
-unhex(S) ->
- unhex(S, []).
+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].
-unhex("", Acc) ->
- list_to_binary(lists:reverse(Acc));
-unhex([D1, D2 | Rest], Acc) ->
- unhex(Rest, [erlang:list_to_integer([D1, D2], 16) | Acc]).
+%% Ignore white space at end of line
+join_entry([<<"-----END ", _/binary>>| Lines], Entry) ->
+ {lists:reverse(Entry), Lines};
+join_entry([Line | Lines], Entry) ->
+ join_entry(Lines, [Line | Entry]).
-decode_key(Data, no_passwd, _Alg, _Salt) ->
- Data;
decode_key(Data, Password, "DES-CBC", Salt) ->
Key = password_to_key(Password, Salt, 8),
IV = Salt,
@@ -171,6 +170,16 @@ decode_key(Data, Password, "DES-EDE3-CBC", Salt) ->
<<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key,
crypto:des_ede3_cbc_decrypt(Key1, Key2, Key3, IV, Data).
+encode_key(Data, Password, "DES-CBC", Salt) ->
+ Key = password_to_key(Password, Salt, 8),
+ IV = Salt,
+ crypto:des_cbc_encrypt(Key, IV, Data);
+encode_key(Data, Password, "DES-EDE3-CBC", Salt) ->
+ Key = password_to_key(Password, Salt, 24),
+ IV = Salt,
+ <<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key,
+ crypto:des_ede3_cbc_encrypt(Key1, Key2, Key3, IV, Data).
+
password_to_key(Data, Salt, KeyLen) ->
<<Key:KeyLen/binary, _/binary>> =
password_to_key(<<>>, Data, Salt, KeyLen, <<>>),
@@ -182,11 +191,69 @@ password_to_key(Prev, Data, Salt, Len, Acc) ->
M = crypto:md5([Prev, Data, Salt]),
password_to_key(M, Data, Salt, Len - size(M), <<Acc/binary, M/binary>>).
-b64encode_and_split(Bin) ->
- split_lines(base64:encode(Bin)).
+unhex(S) ->
+ unhex(S, []).
-split_lines(<<Text:?ENCODED_LINE_LENGTH/binary, Rest/binary>>) ->
- [Text, $\n | split_lines(Rest)];
-split_lines(Bin) ->
- [Bin, $\n].
+unhex("", Acc) ->
+ list_to_binary(lists:reverse(Acc));
+unhex([D1, D2 | Rest], Acc) ->
+ unhex(Rest, [erlang:list_to_integer([D1, D2], 16) | Acc]).
+
+hexify(L) -> [[hex_byte(B)] || B <- binary_to_list(L)].
+
+hex_byte(B) when B < 16#10 -> ["0", erlang:integer_to_list(B, 16)];
+hex_byte(B) -> erlang:integer_to_list(B, 16).
+
+pem_start('Certificate') ->
+ <<"-----BEGIN CERTIFICATE-----">>;
+pem_start('RSAPrivateKey') ->
+ <<"-----BEGIN RSA PRIVATE KEY-----">>;
+pem_start('RSAPublicKey') ->
+ <<"-----BEGIN RSA PUBLIC KEY-----">>;
+pem_start('SubjectPublicKeyInfo') ->
+ <<"-----BEGIN PUBLIC KEY-----">>;
+pem_start('DSAPrivateKey') ->
+ <<"-----BEGIN DSA PRIVATE KEY-----">>;
+pem_start('DHParameter') ->
+ <<"-----BEGIN DH PARAMETERS-----">>.
+pem_end(<<"-----BEGIN CERTIFICATE-----">>) ->
+ <<"-----END CERTIFICATE-----">>;
+pem_end(<<"-----BEGIN RSA PRIVATE KEY-----">>) ->
+ <<"-----END RSA PRIVATE KEY-----">>;
+pem_end(<<"-----BEGIN RSA PUBLIC KEY-----">>) ->
+ <<"-----END RSA PUBLIC KEY-----">>;
+pem_end(<<"-----BEGIN PUBLIC KEY-----">>) ->
+ <<"-----END PUBLIC KEY-----">>;
+pem_end(<<"-----BEGIN DSA PRIVATE KEY-----">>) ->
+ <<"-----END DSA PRIVATE KEY-----">>;
+pem_end(<<"-----BEGIN DH PARAMETERS-----">>) ->
+ <<"-----END DH PARAMETERS-----">>;
+pem_end(_) ->
+ undefined.
+
+asn1_type(<<"-----BEGIN CERTIFICATE-----">>) ->
+ 'Certificate';
+asn1_type(<<"-----BEGIN RSA PRIVATE KEY-----">>) ->
+ 'RSAPrivateKey';
+asn1_type(<<"-----BEGIN RSA PUBLIC KEY-----">>) ->
+ 'RSAPublicKey';
+asn1_type(<<"-----BEGIN PUBLIC KEY-----">>) ->
+ 'SubjectPublicKeyInfo';
+asn1_type(<<"-----BEGIN DSA PRIVATE KEY-----">>) ->
+ 'DSAPrivateKey';
+asn1_type(<<"-----BEGIN DH PARAMETERS-----">>) ->
+ 'DHParameter'.
+
+pem_decrypt() ->
+ <<"Proc-Type: 4,ENCRYPTED">>.
+pem_decrypt_info(Cipher, Salt) ->
+ io_lib:format("DEK-Info: ~s,~s", [Cipher, lists:flatten(hexify(Salt))]).
+
+%%--------------------------------------------------------------------
+%%% Deprecated
+%%--------------------------------------------------------------------
+decode_key({_Type, Bin, not_encrypted}, _) ->
+ Bin;
+decode_key({_Type, Bin, {Chipher,Salt}}, Password) ->
+ decode_key(Bin, Password, Chipher, Salt).
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 edede7c874..1963bd05d4 100644
--- a/lib/public_key/src/public_key.app.src
+++ b/lib/public_key/src/public_key.app.src
@@ -1,10 +1,9 @@
{application, public_key,
[{description, "Public key infrastructure"},
{vsn, "%VSN%"},
- {modules, [
- public_key,
- pubkey_pem,
- pubkey_crypto,
+ {modules, [ public_key,
+ pubkey_pem,
+ pubkey_ssh,
pubkey_cert,
pubkey_cert_records,
'OTP-PUB-KEY'
@@ -13,4 +12,5 @@
{registered, []},
{env, []}
]
-}. \ No newline at end of file
+}.
+
diff --git a/lib/public_key/src/public_key.appup.src b/lib/public_key/src/public_key.appup.src
index 46e5ecca33..4986801dad 100644
--- a/lib/public_key/src/public_key.appup.src
+++ b/lib/public_key/src/public_key.appup.src
@@ -1,39 +1,69 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"0.5",
+ {"0.11",
[
{update, public_key, soft, soft_purge, soft_purge, []},
- {update, pubkey_crypto, 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, []},
+ {update, pubkey_pem, soft, soft_purge, soft_purge, []},
+ {update, pubkey_cert_records, soft, soft_purge, soft_purge, []}
+ ]
+ },
+ {"0.9",
+ [
+ {update, public_key, soft, soft_purge, soft_purge, []},
{update, pubkey_cert, soft, soft_purge, soft_purge, []}
]
},
- {"0.4",
+ {"0.8",
[
+ {update, 'OTP-PUB-KEY', soft, soft_purge, soft_purge, []},
{update, public_key, soft, soft_purge, soft_purge, []},
- {update, pubkey_cert_records, soft, soft_purge, soft_purge, []},
- {update, pubkey_crypto, soft, soft_purge, soft_purge, []},
{update, pubkey_pem, soft, soft_purge, soft_purge, []},
+ {update, pubkey_cert_records, soft, soft_purge, soft_purge, []},
{update, pubkey_cert, soft, soft_purge, soft_purge, []}
- ]
+ ]
}
],
[
- {"0.5",
+ {"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, []},
- {update, pubkey_crypto, soft, soft_purge, soft_purge, []},
{update, pubkey_pem, soft, soft_purge, soft_purge, []},
+ {update, pubkey_cert_records, soft, soft_purge, soft_purge, []}
+ ]
+ },
+ {"0.9",
+ [
+ {update, public_key, soft, soft_purge, soft_purge, []},
{update, pubkey_cert, soft, soft_purge, soft_purge, []}
]
},
- {"0.4",
+ {"0.8",
[
+ {update, 'OTP-PUB-KEY', soft, soft_purge, soft_purge, []},
{update, public_key, soft, soft_purge, soft_purge, []},
- {update, pubkey_cert_records, soft, soft_purge, soft_purge, []},
- {update, pubkey_crypto, soft, soft_purge, soft_purge, []},
{update, pubkey_pem, soft, soft_purge, soft_purge, []},
+ {update, pubkey_cert_records, soft, soft_purge, soft_purge, []},
{update, pubkey_cert, soft, soft_purge, soft_purge, []}
]
}
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 157e76bb21..33fcce2c44 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.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
@@ -23,240 +23,423 @@
-include("public_key.hrl").
--export([decode_private_key/1, decode_private_key/2, decode_dhparams/1,
- decrypt_private/2, decrypt_private/3, encrypt_public/2,
- encrypt_public/3, decrypt_public/2, decrypt_public/3,
- encrypt_private/2, encrypt_private/3, gen_key/1, sign/2, sign/3,
- verify_signature/3, verify_signature/4, verify_signature/5,
- pem_to_der/1, pem_to_der/2, der_to_pem/2,
- pkix_decode_cert/2, pkix_encode_cert/1, pkix_transform/2,
- pkix_is_self_signed/1, pkix_is_fixed_dh_cert/1,
+-export([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,
+ pkix_decode_cert/2, pkix_encode/3,
+ encrypt_private/2, encrypt_private/3,
+ decrypt_private/2, decrypt_private/3,
+ encrypt_public/2, encrypt_public/3,
+ decrypt_public/2, decrypt_public/3,
+ sign/3, verify/4,
+ pkix_sign/2, pkix_verify/2,
+ pkix_is_self_signed/1,
+ pkix_is_fixed_dh_cert/1,
+ pkix_is_issuer/2,
pkix_issuer_id/2,
- pkix_is_issuer/2, pkix_normalize_general_name/1,
- pkix_path_validation/3
+ pkix_normalize_name/1,
+ pkix_path_validation/3,
+ ssh_decode/2, ssh_encode/2
]).
+%% Deprecated
+-export([decode_private_key/1, decode_private_key/2, pem_to_der/1]).
+
+-deprecated({pem_to_der, 1, next_major_release}).
+-deprecated({decode_private_key, 1, next_major_release}).
+-deprecated({decode_private_key, 2, next_major_release}).
+
+-type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
+ | 'rsa_no_padding'.
+-type public_crypt_options() :: [{rsa_pad, rsa_padding()}].
+-type rsa_digest_type() :: 'md5' | 'sha'.
+-type dss_digest_type() :: 'none' | 'sha'.
+
+-define(UINT32(X), X:32/unsigned-big-integer).
+-define(DER_NULL, <<5, 0>>).
+
%%====================================================================
%% API
%%====================================================================
-
%%--------------------------------------------------------------------
-%% Function: decode_private_key(KeyInfo [,Password]) ->
-%% {ok, PrivateKey} | {error, Reason}
+-spec pem_decode(binary()) -> [pem_entry()].
%%
-%% KeyInfo = {Type, der_bin(), ChipherInfo} - as returned from
-%% pem_to_der/[1,2] for private keys
-%% Type = rsa_private_key | dsa_private_key
-%% ChipherInfo = opaque() | no_encryption
-%%
-%% Description: Decodes an asn1 der encoded private key.
+%% Description: Decode PEM binary data and return
+%% entries as asn1 der encoded entities.
%%--------------------------------------------------------------------
-decode_private_key(KeyInfo) ->
- decode_private_key(KeyInfo, no_passwd).
-
-decode_private_key(KeyInfo = {rsa_private_key, _, _}, Password) ->
- DerEncoded = pubkey_pem:decode_key(KeyInfo, Password),
- 'OTP-PUB-KEY':decode('RSAPrivateKey', DerEncoded);
-decode_private_key(KeyInfo = {dsa_private_key, _, _}, Password) ->
- DerEncoded = pubkey_pem:decode_key(KeyInfo, Password),
- 'OTP-PUB-KEY':decode('DSAPrivateKey', DerEncoded).
-
+pem_decode(PemBin) when is_binary(PemBin) ->
+ pubkey_pem:decode(PemBin).
%%--------------------------------------------------------------------
-%% Function: decode_dhparams(DhParamInfo) ->
-%% {ok, DhParams} | {error, Reason}
-%%
-%% DhParamsInfo = {Type, der_bin(), ChipherInfo} - as returned from
-%% pem_to_der/[1,2] for DH parameters.
-%% Type = dh_params
-%% ChipherInfo = opaque() | no_encryption
+-spec pem_encode([pem_entry()]) -> binary().
%%
-%% Description: Decodes an asn1 der encoded DH parameters.
+%% Description: Creates a PEM binary.
%%--------------------------------------------------------------------
-decode_dhparams({dh_params, DerEncoded, not_encrypted}) ->
- 'OTP-PUB-KEY':decode('DHParameter', DerEncoded).
+pem_encode(PemEntries) when is_list(PemEntries) ->
+ iolist_to_binary(pubkey_pem:encode(PemEntries)).
%%--------------------------------------------------------------------
-%% Function: decrypt_private(CipherText, Key) ->
-%% decrypt_private(CipherText, Key, Options) -> PlainTex
-%% decrypt_public(CipherText, Key) ->
-%% decrypt_public(CipherText, Key, Options) -> PlainTex
-%%
-%% CipherText = binary()
-%% Key = rsa_key()
-%% PlainText = binary()
-%%
-%% Description: Decrypts <CipherText>.
+-spec pem_entry_decode(pem_entry(), [string()]) -> term().
+%
+%% Description: Decodes a pem entry. pem_decode/1 returns a list of
+%% pem entries.
+%%--------------------------------------------------------------------
+pem_entry_decode({'SubjectPublicKeyInfo', Der, _}) ->
+ {_, {'AlgorithmIdentifier', AlgId, Params}, {0, Key0}}
+ = der_decode('SubjectPublicKeyInfo', Der),
+ KeyType = pubkey_cert_records:supportedPublicKeyAlgorithms(AlgId),
+ case KeyType of
+ 'RSAPublicKey' ->
+ der_decode(KeyType, Key0);
+ 'DSAPublicKey' ->
+ {params, DssParams} = der_decode('DSAParams', Params),
+ {der_decode(KeyType, Key0), DssParams}
+ end;
+pem_entry_decode({Asn1Type, Der, not_encrypted}) when is_atom(Asn1Type),
+ is_binary(Der) ->
+ der_decode(Asn1Type, Der).
+pem_entry_decode({Asn1Type, Der, not_encrypted}, _) when is_atom(Asn1Type),
+ is_binary(Der) ->
+ der_decode(Asn1Type, Der);
+pem_entry_decode({Asn1Type, CryptDer, {Cipher, Salt}} = PemEntry,
+ Password) when is_atom(Asn1Type),
+ is_binary(CryptDer),
+ is_list(Cipher),
+ is_binary(Salt),
+ erlang:byte_size(Salt) == 8
+ ->
+ Der = pubkey_pem:decipher(PemEntry, Password),
+ der_decode(Asn1Type, Der).
+
+%%--------------------------------------------------------------------
+-spec pem_entry_encode(pki_asn1_type(), term()) -> pem_entry().
+-spec pem_entry_encode(pki_asn1_type(), term(),
+ {{Cipher :: string(), Salt :: binary()}, string()}) ->
+ pem_entry().
+%
+%% Description: Creates a pem entry that can be feed to pem_encode/1.
+%%--------------------------------------------------------------------
+pem_entry_encode('SubjectPublicKeyInfo', Entity=#'RSAPublicKey'{}) ->
+ Der = der_encode('RSAPublicKey', Entity),
+ Spki = {'SubjectPublicKeyInfo',
+ {'AlgorithmIdentifier', ?'rsaEncryption', ?DER_NULL}, {0, Der}},
+ pem_entry_encode('SubjectPublicKeyInfo', Spki);
+pem_entry_encode('SubjectPublicKeyInfo',
+ {DsaInt, Params=#'Dss-Parms'{}}) when is_integer(DsaInt) ->
+ KeyDer = der_encode('DSAPublicKey', DsaInt),
+ ParamDer = der_encode('DSAParams', {params, Params}),
+ Spki = {'SubjectPublicKeyInfo',
+ {'AlgorithmIdentifier', ?'id-dsa', ParamDer}, {0, KeyDer}},
+ pem_entry_encode('SubjectPublicKeyInfo', Spki);
+pem_entry_encode(Asn1Type, Entity) when is_atom(Asn1Type) ->
+ Der = der_encode(Asn1Type, Entity),
+ {Asn1Type, Der, not_encrypted}.
+pem_entry_encode(Asn1Type, Entity,
+ {{Cipher, Salt}= CipherInfo, Password}) when is_atom(Asn1Type),
+ is_list(Cipher),
+ is_binary(Salt),
+ erlang:byte_size(Salt) == 8,
+ is_list(Password)->
+ Der = der_encode(Asn1Type, Entity),
+ DecryptDer = pubkey_pem:cipher(Der, CipherInfo, Password),
+ {Asn1Type, DecryptDer, CipherInfo}.
+
+%%--------------------------------------------------------------------
+-spec der_decode(asn1_type(), Der::binary()) -> term().
+%%
+%% Description: Decodes a public key asn1 der encoded entity.
+%%--------------------------------------------------------------------
+der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) ->
+ try
+ {ok, Decoded} = 'OTP-PUB-KEY':decode(Asn1Type, Der),
+ Decoded
+ catch
+ error:{badmatch, {error, _}} = Error ->
+ erlang:error(Error)
+ end.
+
+%%--------------------------------------------------------------------
+-spec der_encode(asn1_type(), term()) -> Der::binary().
+%%
+%% Description: Encodes a public key entity with asn1 DER encoding.
+%%--------------------------------------------------------------------
+der_encode(Asn1Type, Entity) when is_atom(Asn1Type) ->
+ try
+ {ok, Encoded} = 'OTP-PUB-KEY':encode(Asn1Type, Entity),
+ iolist_to_binary(Encoded)
+ catch
+ error:{badmatch, {error, _}} = Error ->
+ erlang:error(Error)
+ end.
+
+%%--------------------------------------------------------------------
+-spec pkix_decode_cert(Cert::binary(), plain | otp) ->
+ #'Certificate'{} | #'OTPCertificate'{}.
+%%
+%% Description: Decodes an asn1 der encoded pkix certificate. The otp
+%% option will use the customized asn1 specification OTP-PKIX.asn1 for
+%% decoding and also recursively decode most of the standard
+%% extensions.
+%% --------------------------------------------------------------------
+pkix_decode_cert(DerCert, plain) when is_binary(DerCert) ->
+ der_decode('Certificate', DerCert);
+pkix_decode_cert(DerCert, otp) when is_binary(DerCert) ->
+ try
+ {ok, #'OTPCertificate'{}= Cert} =
+ pubkey_cert_records:decode_cert(DerCert),
+ Cert
+ catch
+ error:{badmatch, {error, _}} = Error ->
+ erlang:error(Error)
+ end.
+
+%%--------------------------------------------------------------------
+-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
+%% that are decoded with the otp format, whereas for the plain format this
+%% function will only call der_encode/2.
+%%--------------------------------------------------------------------
+pkix_encode(Asn1Type, Term, plain) when is_atom(Asn1Type) ->
+ der_encode(Asn1Type, Term);
+
+pkix_encode(Asn1Type, Term0, otp) when is_atom(Asn1Type) ->
+ Term = pubkey_cert_records:transform(Term0, encode),
+ der_encode(Asn1Type, Term).
+
+%%--------------------------------------------------------------------
+-spec decrypt_private(CipherText :: binary(), rsa_private_key()) ->
+ PlainText :: binary().
+-spec decrypt_private(CipherText :: binary(), rsa_private_key(),
+ public_crypt_options()) -> PlainText :: binary().
+%%
+%% Description: Public key decryption using the private key.
%%--------------------------------------------------------------------
decrypt_private(CipherText, Key) ->
decrypt_private(CipherText, Key, []).
-decrypt_private(CipherText, Key, Options) ->
- Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
- pubkey_crypto:decrypt_private(CipherText, Key, Padding).
-decrypt_public(CipherText, Key) ->
- decrypt_public(CipherText, Key, []).
-decrypt_public(CipherText, Key, Options) ->
+decrypt_private(CipherText,
+ #'RSAPrivateKey'{modulus = N,publicExponent = E,
+ privateExponent = D},
+ Options) when is_binary(CipherText),
+ is_list(Options) ->
Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
- pubkey_crypto:decrypt_public(CipherText, Key, Padding).
+ crypto:rsa_private_decrypt(CipherText,
+ [crypto:mpint(E), crypto:mpint(N),
+ crypto:mpint(D)], Padding).
%%--------------------------------------------------------------------
-%% Function: encrypt_public(PlainText, Key, Options) -> CipherText
-%% encrypt_private(PlainText, Key, Options) -> CipherText
-%%
-%% PlainText = iolist()
-%% Key = rsa_private_key()
-%% CipherText = binary()
+-spec decrypt_public(CipherText :: binary(), rsa_public_key() | rsa_private_key()) ->
+ PlainText :: binary().
+-spec decrypt_public(CipherText :: binary(), rsa_public_key() | rsa_private_key(),
+ public_crypt_options()) -> PlainText :: binary().
+%% NOTE: The rsa_private_key() is not part of the documented API it is
+%% here for testing purposes, in a real situation this is not a relevant
+%% thing to do.
%%
-%% Description: Encrypts <Plain>
+%% Description: Public key decryption using the public key.
%%--------------------------------------------------------------------
-encrypt_public(PlainText, Key) ->
- encrypt_public(PlainText, Key, []).
-encrypt_public(PlainText, Key, Options) ->
- Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_oaep_padding),
- pubkey_crypto:encrypt_public(PlainText, Key, Padding).
+decrypt_public(CipherText, Key) ->
+ decrypt_public(CipherText, Key, []).
-encrypt_private(PlainText, Key) ->
- encrypt_private(PlainText, Key, []).
-encrypt_private(PlainText, Key, Options) ->
- Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_oaep_padding),
- pubkey_crypto:encrypt_private(PlainText, Key, Padding).
+decrypt_public(CipherText, #'RSAPublicKey'{modulus = N, publicExponent = E},
+ Options) when is_binary(CipherText), is_list(Options) ->
+ decrypt_public(CipherText, N,E, Options);
-%%--------------------------------------------------------------------
-%% Function: gen_key(Params) -> Keys
-%%
-%% Params = #'DomainParameters'{} - Currently only supported option
-%% Keys = {PublicDHKey = integer(), PrivateDHKey = integer()}
-%%
-%% Description: Generates keys. Currently supports Diffie-Hellman keys.
-%%--------------------------------------------------------------------
-gen_key(#'DHParameter'{prime = P, base = G}) when is_integer(P),
- is_integer(G) ->
- pubkey_crypto:gen_key(diffie_hellman, [P, G]).
+decrypt_public(CipherText,#'RSAPrivateKey'{modulus = N, publicExponent = E},
+ Options) when is_binary(CipherText), is_list(Options) ->
+ decrypt_public(CipherText, N,E, Options).
%%--------------------------------------------------------------------
-%% Function: pem_to_der(CertSource) ->
-%% pem_to_der(CertSource, Password) -> {ok, [Entry]} |
-%% {error, Reason}
-%%
-%% CertSource = File | CertData
-%% CertData = binary()
-%% File = path()
-%% Entry = {entry_type(), der_bin(), ChipherInfo}
-%% ChipherInfo = opague() | no_encryption
-%% der_bin() = binary()
-%% entry_type() = cert | cert_req | rsa_private_key | dsa_private_key
-%% dh_params
+-spec encrypt_public(PlainText :: binary(), rsa_public_key() | rsa_private_key()) ->
+ CipherText :: binary().
+-spec encrypt_public(PlainText :: binary(), rsa_public_key() | rsa_private_key(),
+ public_crypt_options()) -> CipherText :: binary().
+
+%% NOTE: The rsa_private_key() is not part of the documented API it is
+%% here for testing purposes, in a real situation this is not a relevant
+%% thing to do.
%%
-%% Description: decode PEM binary data or a PEM file and return
-%% entries as asn1 der encoded entities. Currently supported entry
-%% types are certificates, certificate requests, rsa private keys and
-%% dsa private keys. In the case of a key entry ChipherInfo will be
-%% private keys and Diffie Hellam parameters .In the case of a key
-%% entry ChipherInfo will be used by decode_private_key/2 if the key
-%% is protected by a password.
+%% Description: Public key encryption using the public key.
%%--------------------------------------------------------------------
-pem_to_der(CertSource) ->
- pem_to_der(CertSource, no_passwd).
+encrypt_public(PlainText, Key) ->
+ encrypt_public(PlainText, Key, []).
-pem_to_der(File, Password) when is_list(File) ->
- pubkey_pem:read_file(File, Password);
-pem_to_der(PemBin, Password) when is_binary(PemBin) ->
- pubkey_pem:decode(PemBin, Password).
+encrypt_public(PlainText, #'RSAPublicKey'{modulus=N,publicExponent=E},
+ Options) when is_binary(PlainText), is_list(Options) ->
+ encrypt_public(PlainText, N,E, Options);
-der_to_pem(File, TypeDerList) ->
- pubkey_pem:write_file(File, TypeDerList).
+encrypt_public(PlainText, #'RSAPrivateKey'{modulus=N,publicExponent=E},
+ Options) when is_binary(PlainText), is_list(Options) ->
+ encrypt_public(PlainText, N,E, Options).
%%--------------------------------------------------------------------
-%% Function: pkix_decode_cert(BerCert, Type) -> {ok, Cert} | {error, Reason}
+-spec encrypt_private(PlainText :: binary(), rsa_private_key()) ->
+ CipherText :: binary().
+-spec encrypt_private(PlainText :: binary(), rsa_private_key(),
+ public_crypt_options()) -> CipherText :: binary().
%%
-%% BerCert = binary()
-%% Type = plain | otp
-%% Cert = certificate()
-%%
-%% Description: Decodes an asn1 ber encoded pkix certificate.
-%% otp - Uses OTP-PKIX.asn1 to decode known extensions and
-%% enhance the signature field in #'Certificate'{} and '#TBSCertificate'{}.
+%% Description: Public key encryption using the private key.
%%--------------------------------------------------------------------
-pkix_decode_cert(BinCert, Type) ->
- pubkey_cert_records:decode_cert(BinCert, Type).
+encrypt_private(PlainText, Key) ->
+ encrypt_private(PlainText, Key, []).
+
+encrypt_private(PlainText, #'RSAPrivateKey'{modulus = N,
+ publicExponent = E,
+ privateExponent = D},
+ Options) when is_binary(PlainText), is_list(Options) ->
+ Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
+ crypto:rsa_private_encrypt(PlainText, [crypto:mpint(E),
+ crypto:mpint(N),
+ crypto:mpint(D)], Padding).
%%--------------------------------------------------------------------
-%% Function: pkix_encode_cert(Cert) -> {ok, binary()} | {error, Reason}
+-spec sign(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(),
+ rsa_private_key() |
+ dsa_private_key()) -> Signature :: binary().
%%
-%% Cert = #'Certificate'{}
-%%
-%% Description: Encodes a certificate record using asn1.
+%% Description: Create digital signature.
%%--------------------------------------------------------------------
-pkix_encode_cert(Cert) ->
- pubkey_cert_records:encode_cert(Cert).
+sign(PlainText, DigestType, #'RSAPrivateKey'{modulus = N, publicExponent = E,
+ privateExponent = D})
+ when is_binary(PlainText),
+ (DigestType == md5 orelse
+ DigestType == sha) ->
+
+ crypto:rsa_sign(DigestType, sized_binary(PlainText), [crypto:mpint(E),
+ crypto:mpint(N),
+ crypto:mpint(D)]);
+
+sign(Digest, none, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X})
+ when is_binary(Digest)->
+ crypto:dss_sign(none, Digest,
+ [crypto:mpint(P), crypto:mpint(Q),
+ crypto:mpint(G), crypto:mpint(X)]);
+
+sign(PlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X})
+ when is_binary(PlainText) ->
+ crypto:dss_sign(sized_binary(PlainText),
+ [crypto:mpint(P), crypto:mpint(Q),
+ crypto:mpint(G), crypto:mpint(X)]).
+
+%%--------------------------------------------------------------------
+-spec verify(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(),
+ Signature :: binary(), rsa_public_key()
+ | dsa_public_key()) -> boolean().
+%%
+%% Description: Verifies a digital signature.
+%%--------------------------------------------------------------------
+verify(PlainText, DigestType, Signature,
+ #'RSAPublicKey'{modulus = Mod, publicExponent = Exp})
+ when is_binary (PlainText), DigestType == sha; DigestType == md5 ->
+ crypto:rsa_verify(DigestType,
+ sized_binary(PlainText),
+ sized_binary(Signature),
+ [crypto:mpint(Exp), crypto:mpint(Mod)]);
+
+verify(Digest, none, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}})
+ when is_integer(Key), is_binary(Digest), is_binary(Signature) ->
+ crypto:dss_verify(none,
+ Digest,
+ sized_binary(Signature),
+ [crypto:mpint(P), crypto:mpint(Q),
+ crypto:mpint(G), crypto:mpint(Key)]);
+verify(PlainText, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}})
+ when is_integer(Key), is_binary(PlainText), is_binary(Signature) ->
+ crypto:dss_verify(sized_binary(PlainText),
+ sized_binary(Signature),
+ [crypto:mpint(P), crypto:mpint(Q),
+ crypto:mpint(G), crypto:mpint(Key)]).
%%--------------------------------------------------------------------
-%% Function: pkix_transform(CertPart, Op) -> TransformedCertPart
+-spec pkix_sign(#'OTPTBSCertificate'{},
+ rsa_private_key() | dsa_private_key()) -> Der::binary().
%%
-%% CertPart = pkix part data
-%% Op = encode | decode
-%%
-%% Description: Transform parts of a pkix certificate between 'plain' format
-%% and the internal 'otp' format, see pkix_decode_cert/2.
-%% Decode transforms from 'plain' to 'otp' and encode from 'otp' to 'plain'
-%% format.
+%% Description: Sign a pkix x.509 certificate. Returns the corresponding
+%% der encoded 'Certificate'{}
%%--------------------------------------------------------------------
-pkix_transform(CertPart, Op) ->
- pubkey_cert_records:transform(CertPart, Op).
+pkix_sign(#'OTPTBSCertificate'{signature =
+ #'SignatureAlgorithm'{algorithm = Alg}
+ = SigAlg} = TBSCert, Key) ->
+
+ Msg = pkix_encode('OTPTBSCertificate', TBSCert, otp),
+ DigestType = pubkey_cert:digest_type(Alg),
+ Signature = sign(Msg, DigestType, Key),
+ Cert = #'OTPCertificate'{tbsCertificate= TBSCert,
+ signatureAlgorithm = SigAlg,
+ signature = {0, Signature}
+ },
+ pkix_encode('OTPCertificate', Cert, otp).
%%--------------------------------------------------------------------
-%% Function: pkix_path_validation(TrustedCert, CertChain, Options) ->
-%% {ok, {{algorithm(), public_key(), public_key_params()} policy_tree()}} |
-%% {error, Reason}
+-spec pkix_verify(Cert::binary(), rsa_public_key()|
+ dsa_public_key()) -> boolean().
%%
-%% Description: Performs a bacis path validation according to RFC 3280.
+%% Description: Verify pkix x.509 certificate signature.
%%--------------------------------------------------------------------
-pkix_path_validation(TrustedCert, CertChain, Options)
- when is_binary(TrustedCert) ->
- {ok, OtpCert} = pkix_decode_cert(TrustedCert, otp),
- pkix_path_validation(OtpCert, CertChain, Options);
+pkix_verify(DerCert, {Key, #'Dss-Parms'{}} = DSAKey)
+ when is_binary(DerCert), is_integer(Key) ->
+ {DigestType, PlainText, Signature} = pubkey_cert:verify_data(DerCert),
+ verify(PlainText, DigestType, Signature, DSAKey);
-pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options)
- when is_list(CertChain), is_list(Options) ->
- MaxPathDefault = length(CertChain),
- ValidationState = pubkey_cert:init_validation_state(TrustedCert,
- MaxPathDefault,
- Options),
- Fun = proplists:get_value(validate_extensions_fun, Options,
- fun(Extensions, State, _, AccError) ->
- {Extensions, State, AccError}
- end),
- Verify = proplists:get_value(verify, Options, true),
- path_validation(CertChain, ValidationState, Fun, Verify).
-%%--------------------------------------------------------------------
-%% Function: pkix_is_fixed_dh_cert(Cert) -> true | false
+pkix_verify(DerCert, #'RSAPublicKey'{} = RSAKey)
+ when is_binary(DerCert) ->
+ {DigestType, PlainText, Signature} = pubkey_cert:verify_data(DerCert),
+ verify(PlainText, DigestType, Signature, RSAKey).
+
+%%--------------------------------------------------------------------
+-spec pkix_is_issuer(Cert::binary()| #'OTPCertificate'{},
+ IssuerCert::binary()|
+ #'OTPCertificate'{}) -> boolean().
%%
-%% Description: Checks if a Certificate is a fixed Diffie-Hellman Cert
+%% Description: Checks if <IssuerCert> issued <Cert>.
%%--------------------------------------------------------------------
-pkix_is_fixed_dh_cert(#'OTPCertificate'{} = OTPCert) ->
- pubkey_cert:is_fixed_dh_cert(OTPCert);
-pkix_is_fixed_dh_cert(Cert) when is_binary(Cert) ->
- {ok, OtpCert} = pkix_decode_cert(Cert, otp),
- pkix_is_fixed_dh_cert(OtpCert).
+pkix_is_issuer(Cert, IssuerCert) when is_binary(Cert) ->
+ OtpCert = pkix_decode_cert(Cert, otp),
+ pkix_is_issuer(OtpCert, IssuerCert);
+pkix_is_issuer(Cert, IssuerCert) when is_binary(IssuerCert) ->
+ OtpIssuerCert = pkix_decode_cert(IssuerCert, otp),
+ pkix_is_issuer(Cert, OtpIssuerCert);
+pkix_is_issuer(#'OTPCertificate'{tbsCertificate = TBSCert},
+ #'OTPCertificate'{tbsCertificate = Candidate}) ->
+ pubkey_cert:is_issuer(TBSCert#'OTPTBSCertificate'.issuer,
+ Candidate#'OTPTBSCertificate'.subject).
%%--------------------------------------------------------------------
-%% Function: pkix_is_self_signed(Cert) -> true | false
+-spec pkix_is_self_signed(Cert::binary()| #'OTPCertificate'{}) -> boolean().
%%
%% Description: Checks if a Certificate is self signed.
%%--------------------------------------------------------------------
pkix_is_self_signed(#'OTPCertificate'{} = OTPCert) ->
pubkey_cert:is_self_signed(OTPCert);
pkix_is_self_signed(Cert) when is_binary(Cert) ->
- {ok, OtpCert} = pkix_decode_cert(Cert, otp),
+ OtpCert = pkix_decode_cert(Cert, otp),
pkix_is_self_signed(OtpCert).
-
+
%%--------------------------------------------------------------------
-%% Function: pkix_issuer_id(Cert) -> {ok, {SerialNr, Issuer}} | {error, Reason}
-%%
-%% Cert = asn1_der_encoded() | 'OTPCertificate'{}
+-spec pkix_is_fixed_dh_cert(Cert::binary()| #'OTPCertificate'{}) -> boolean().
%%
-%% Description: Returns the issuer id.
+%% Description: Checks if a Certificate is a fixed Diffie-Hellman Cert.
+%%--------------------------------------------------------------------
+pkix_is_fixed_dh_cert(#'OTPCertificate'{} = OTPCert) ->
+ pubkey_cert:is_fixed_dh_cert(OTPCert);
+pkix_is_fixed_dh_cert(Cert) when is_binary(Cert) ->
+ OtpCert = pkix_decode_cert(Cert, otp),
+ pkix_is_fixed_dh_cert(OtpCert).
+
+%%--------------------------------------------------------------------
+-spec pkix_issuer_id(Cert::binary()| #'OTPCertificate'{},
+ IssuedBy :: self | other) ->
+ {ok, {SerialNr :: integer(),
+ Issuer :: {rdnSequence,
+ [#'AttributeTypeAndValue'{}]}}}
+ | {error, Reason :: term()}.
+%
+%% Description: Returns the issuer id.
%%--------------------------------------------------------------------
pkix_issuer_id(#'OTPCertificate'{} = OtpCert, self) ->
pubkey_cert:issuer_id(OtpCert, self);
@@ -265,151 +448,143 @@ pkix_issuer_id(#'OTPCertificate'{} = OtpCert, other) ->
pubkey_cert:issuer_id(OtpCert, other);
pkix_issuer_id(Cert, Signed) when is_binary(Cert) ->
- {ok, OtpCert} = pkix_decode_cert(Cert, otp),
+ OtpCert = pkix_decode_cert(Cert, otp),
pkix_issuer_id(OtpCert, Signed).
%%--------------------------------------------------------------------
-%% Function: pkix_is_issuer(Cert, IssuerCert) -> true | false
+-spec pkix_normalize_name({rdnSequence,
+ [#'AttributeTypeAndValue'{}]}) ->
+ {rdnSequence,
+ [#'AttributeTypeAndValue'{}]}.
%%
-%% Cert = asn1_der_encoded() | 'OTPCertificate'{}
-%% IssuerCert = asn1_der_encoded() | 'OTPCertificate'{}
-%%
-%% Description: Checks if <IssuerCert> issued <Cert>.
+%% Description: Normalizes a issuer name so that it can be easily
+%% compared to another issuer name.
%%--------------------------------------------------------------------
-pkix_is_issuer(Cert, IssuerCert) when is_binary(Cert) ->
- {ok, OtpCert} = pkix_decode_cert(Cert, otp),
- pkix_is_issuer(OtpCert, IssuerCert);
+pkix_normalize_name(Issuer) ->
+ pubkey_cert:normalize_general_name(Issuer).
-pkix_is_issuer(Cert, IssuerCert) when is_binary(IssuerCert) ->
- {ok, OtpIssuerCert} = pkix_decode_cert(IssuerCert, otp),
- pkix_is_issuer(Cert, OtpIssuerCert);
+%%--------------------------------------------------------------------
+-spec pkix_path_validation(Cert::binary()| #'OTPCertificate'{} | atom(),
+ CertChain :: [binary()] ,
+ Options :: list()) ->
+ {ok, {PublicKeyInfo :: term(),
+ PolicyTree :: term()}} |
+ {error, {bad_cert, Reason :: term()}}.
+%% Description: Performs a basic path validation according to RFC 5280.
+%%--------------------------------------------------------------------
+pkix_path_validation(PathErr, [Cert | Chain], Options0) when is_atom(PathErr)->
+ {VerifyFun, Userstat0} =
+ proplists:get_value(verify_fun, Options0, ?DEFAULT_VERIFYFUN),
+ Otpcert = pkix_decode_cert(Cert, otp),
+ Reason = {bad_cert, PathErr},
+ try VerifyFun(Otpcert, Reason, Userstat0) of
+ {valid, Userstate} ->
+ Options = proplists:delete(verify_fun, Options0),
+ pkix_path_validation(Otpcert, Chain, [{verify_fun,
+ {VerifyFun, Userstate}}| Options]);
+ {fail, _} ->
+ {error, Reason}
+ catch
+ _:_ ->
+ {error, Reason}
+ end;
+pkix_path_validation(TrustedCert, CertChain, Options)
+ when is_binary(TrustedCert) ->
+ OtpCert = pkix_decode_cert(TrustedCert, otp),
+ pkix_path_validation(OtpCert, CertChain, Options);
-pkix_is_issuer(#'OTPCertificate'{tbsCertificate = TBSCert},
- #'OTPCertificate'{tbsCertificate = Candidate}) ->
- pubkey_cert:is_issuer(TBSCert#'OTPTBSCertificate'.issuer,
- Candidate#'OTPTBSCertificate'.subject).
-
-%%--------------------------------------------------------------------
-%% Function: pkix_normalize_general_name(Issuer) ->
-%%
-%% Issuer = general_name() - see PKIX
-%%
-%% Description: Normalizes a general name so that it can be easily
-%% compared to another genral name.
-%%--------------------------------------------------------------------
-pkix_normalize_general_name(Issuer) ->
- pubkey_cert:normalize_general_name(Issuer).
+pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options)
+ when is_list(CertChain), is_list(Options) ->
+ MaxPathDefault = length(CertChain),
+ ValidationState = pubkey_cert:init_validation_state(TrustedCert,
+ MaxPathDefault,
+ Options),
+ path_validation(CertChain, ValidationState).
%%--------------------------------------------------------------------
-%% Function:sign(Msg, Key) -> {ok, Signature}
-%% sign(Msg, Key, KeyParams) -> {ok, Signature}
-%%
-%% Msg = binary() | #'TBSCertificate'{}
-%% Key = private_key()
-%% KeyParams = key_params()
-%% Signature = binary()
+-spec ssh_decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}].
%%
-%% Description: Signs plaintext Msg or #TBSCertificate{}, in the later
-%% case a der encoded "#Certificate{}" will be returned.
+%% 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.
%%--------------------------------------------------------------------
-sign(Msg, #'RSAPrivateKey'{} = Key) when is_binary(Msg) ->
- pubkey_crypto:sign(Msg, Key);
-
-sign(Msg, #'DSAPrivateKey'{} = Key) when is_binary(Msg) ->
- pubkey_crypto:sign(Msg, Key);
-
-sign(#'OTPTBSCertificate'{signature = #'SignatureAlgorithm'{algorithm = Alg}
- = SigAlg} = TBSCert, Key) ->
- Msg = pubkey_cert_records:encode_tbs_cert(TBSCert),
- DigestType = pubkey_cert:digest_type(Alg),
- Signature = pubkey_crypto:sign(DigestType, Msg, Key),
- Cert = #'OTPCertificate'{tbsCertificate= TBSCert,
- signatureAlgorithm = SigAlg,
- signature = {0, Signature}
- },
- pkix_encode_cert(Cert).
-
-sign(DigestType, Msg, Key) ->
- pubkey_crypto:sign(DigestType, Msg, Key).
+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).
%%--------------------------------------------------------------------
-%% Function: verify_signature(PlainText, DigestType, Signature, Key) ->
-%% verify_signature(PlainText, DigestType,
-%% Signature, Key, KeyParams) ->
-%% verify_signature(DerCert, Key, KeyParams) ->
-%%
-%% PlainText = binary()
-%% DigestType = md5 | sha
-%% DerCert = asn1_der_encoded()
-%% Signature = binary()
-%% Key = public_key()
-%% KeyParams = key_params()
-%% Verified = boolean()
-%%
-%% Description: Verifies the signature <Signature>.
-%%--------------------------------------------------------------------
-verify_signature(PlainText, DigestType, Signature, #'RSAPublicKey'{} = Key)
- when is_binary(PlainText), is_binary(Signature), DigestType == sha;
- DigestType == md5 ->
- pubkey_crypto:verify(DigestType, PlainText, Signature, Key, undefined).
-
-verify_signature(PlainText, DigestType, Signature, #'RSAPublicKey'{} = Key,
- KeyParams)
- when is_binary(PlainText), is_binary(Signature), DigestType == sha;
- DigestType == md5 ->
- pubkey_crypto:verify(DigestType, PlainText, Signature, Key, KeyParams);
-verify_signature(PlainText, sha, Signature, Key, #'Dss-Parms'{} = KeyParams)
- when is_binary(PlainText), is_binary(Signature), is_integer(Key) ->
- pubkey_crypto:verify(sha, PlainText, Signature, Key, KeyParams).
-
-verify_signature(DerCert, Key, #'Dss-Parms'{} = KeyParams)
- when is_binary(DerCert), is_integer(Key) ->
- pubkey_cert:verify_signature(DerCert, Key, KeyParams);
-verify_signature(DerCert, #'RSAPublicKey'{} = Key, KeyParams)
- when is_binary(DerCert) ->
- pubkey_cert:verify_signature(DerCert, Key, KeyParams).
+-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
%%--------------------------------------------------------------------
+
+encrypt_public(PlainText, N, E, Options)->
+ Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
+ crypto:rsa_public_encrypt(PlainText, [crypto:mpint(E),crypto:mpint(N)],
+ Padding).
+
+decrypt_public(CipherText, N,E, Options) ->
+ Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
+ 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 =
PublicKey,
working_public_key_parameters
= PublicKeyParams,
- valid_policy_tree = Tree,
- acc_errors = AccErrors
- }, _, _) ->
- {ok, {{Algorithm, PublicKey, PublicKeyParams}, Tree, AccErrors}};
+ valid_policy_tree = Tree
+ }) ->
+ {ok, {{Algorithm, PublicKey, PublicKeyParams}, Tree}};
path_validation([DerCert | Rest], ValidationState = #path_validation_state{
- max_path_length = Len},
- Fun, Verify) when Len >= 0 ->
- try validate(DerCert,
- ValidationState#path_validation_state{last_cert=Rest=:=[]},
- Fun, Verify) of
+ max_path_length = Len}) when Len >= 0 ->
+ try validate(DerCert,
+ ValidationState#path_validation_state{last_cert=Rest=:=[]}) of
#path_validation_state{} = NewValidationState ->
- path_validation(Rest, NewValidationState, Fun, Verify)
+ path_validation(Rest, NewValidationState)
catch
throw:Reason ->
{error, Reason}
end;
-path_validation(_, _, _, true) ->
- {error, {bad_cert, max_path_length_reached}};
-
-path_validation(_, #path_validation_state{working_public_key_algorithm
- = Algorithm,
- working_public_key =
- PublicKey,
- working_public_key_parameters
- = PublicKeyParams,
- valid_policy_tree = Tree,
- acc_errors = AccErrors
- }, _, false) ->
- {ok, {{Algorithm, PublicKey, PublicKeyParams}, Tree,
- [{bad_cert, max_path_length_reached}|AccErrors]}}.
+path_validation([DerCert | _] = Path,
+ #path_validation_state{user_state = UserState0,
+ verify_fun = VerifyFun} =
+ ValidationState) ->
+ Reason = {bad_cert, max_path_length_reached},
+ OtpCert = pkix_decode_cert(DerCert, otp),
+ try VerifyFun(OtpCert, Reason, UserState0) of
+ {valid, UserState} ->
+ path_validation(Path,
+ ValidationState#path_validation_state{
+ max_path_length = 0,
+ user_state = UserState});
+ {fail, _} ->
+ {error, Reason}
+ catch
+ _:_ ->
+ {error, Reason}
+ end.
validate(DerCert, #path_validation_state{working_issuer_name = Issuer,
working_public_key = Key,
@@ -419,38 +594,58 @@ validate(DerCert, #path_validation_state{working_issuer_name = Issuer,
excluded_subtrees = Exclude,
last_cert = Last,
user_state = UserState0,
- acc_errors = AccErr0} =
- ValidationState0, ValidateExtensionFun, Verify) ->
- {ok, OtpCert} = pkix_decode_cert(DerCert, otp),
- %% All validate functions will throw {bad_cert, Reason} if they
- %% fail and Verify = true if Verify = false errors
- %% will be accumulated in the validationstate
- AccErr1 = pubkey_cert:validate_time(OtpCert, AccErr0, Verify),
-
- AccErr2 = pubkey_cert:validate_issuer(OtpCert, Issuer, AccErr1, Verify),
-
- AccErr3 = pubkey_cert:validate_names(OtpCert, Permit, Exclude, Last,
- AccErr2, Verify),
- AccErr4 =
- pubkey_cert:validate_revoked_status(OtpCert, Verify, AccErr3),
+ verify_fun = VerifyFun} =
+ ValidationState0) ->
+
+ OtpCert = pkix_decode_cert(DerCert, otp),
+
+ UserState1 = pubkey_cert:validate_time(OtpCert, UserState0, VerifyFun),
+
+ UserState2 = pubkey_cert:validate_issuer(OtpCert, Issuer, UserState1, VerifyFun),
+
+ UserState3 = pubkey_cert:validate_names(OtpCert, Permit, Exclude, Last,
+ UserState2,VerifyFun),
+
+ UserState4 = pubkey_cert:validate_revoked_status(OtpCert, UserState3, VerifyFun),
- {ValidationState1, UnknownExtensions0, AccErr5} =
- pubkey_cert:validate_extensions(OtpCert, ValidationState0, Verify,
- AccErr4),
- %% We want the key_usage extension to be checked before we validate
+ {ValidationState1, UserState5} =
+ pubkey_cert:validate_extensions(OtpCert, ValidationState0, UserState4,
+ VerifyFun),
+
+ %% We want the key_usage extension to be checked before we validate
%% the signature.
- AccErr6 =
- pubkey_cert:validate_signature(OtpCert, DerCert, Key, KeyParams,
- AccErr5, Verify),
-
- {UnknownExtensions, UserState, AccErr7} =
- ValidateExtensionFun(UnknownExtensions0, UserState0, Verify, AccErr6),
-
- %% Check that all critical extensions have been handled
- AccErr =
- pubkey_cert:validate_unknown_extensions(UnknownExtensions, AccErr7,
- Verify),
+ UserState6 = pubkey_cert:validate_signature(OtpCert, DerCert,
+ Key, KeyParams, UserState5, VerifyFun),
+ UserState = case Last of
+ false ->
+ pubkey_cert:verify_fun(OtpCert, valid, UserState6, VerifyFun);
+ true ->
+ pubkey_cert:verify_fun(OtpCert, valid_peer,
+ UserState6, VerifyFun)
+ end,
+
ValidationState =
- ValidationState1#path_validation_state{user_state = UserState,
- acc_errors = AccErr},
+ ValidationState1#path_validation_state{user_state = UserState},
+
pubkey_cert:prepare_for_next_cert(OtpCert, ValidationState).
+
+sized_binary(Binary) ->
+ Size = size(Binary),
+ <<?UINT32(Size), Binary/binary>>.
+
+%%--------------------------------------------------------------------
+%%% Deprecated functions
+%%--------------------------------------------------------------------
+pem_to_der(CertSource) ->
+ {ok, Bin} = file:read_file(CertSource),
+ {ok, pubkey_pem:decode(Bin)}.
+
+decode_private_key(KeyInfo) ->
+ decode_private_key(KeyInfo, no_passwd).
+
+decode_private_key(KeyInfo = {'RSAPrivateKey', _, _}, Password) ->
+ DerEncoded = pubkey_pem:decode_key(KeyInfo, Password),
+ 'OTP-PUB-KEY':decode('RSAPrivateKey', DerEncoded);
+decode_private_key(KeyInfo = {'DSAPrivateKey', _, _}, Password) ->
+ DerEncoded = pubkey_pem:decode_key(KeyInfo, Password),
+ 'OTP-PUB-KEY':decode('DSAPrivateKey', DerEncoded).
diff --git a/lib/public_key/test/Makefile b/lib/public_key/test/Makefile
index c7215020c7..6889ae9a8a 100644
--- a/lib/public_key/test/Makefile
+++ b/lib/public_key/test/Makefile
@@ -28,6 +28,7 @@ INCLUDES= -I. -I ../include
# ----------------------------------------------------
MODULES= \
+ erl_make_certs \
public_key_SUITE \
pkits_SUITE
@@ -40,6 +41,9 @@ TARGET_FILES= \
SPEC_FILES = public_key.spec
+COVER_FILE = public_key.cover
+
+
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -74,9 +78,9 @@ release_spec: opt
release_tests_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(HRL_FILES)$(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/erl_make_certs.erl b/lib/public_key/test/erl_make_certs.erl
new file mode 100644
index 0000000000..8b01ca3ad4
--- /dev/null
+++ b/lib/public_key/test/erl_make_certs.erl
@@ -0,0 +1,421 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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%
+%%
+
+%% Create test certificates
+
+-module(erl_make_certs).
+-include_lib("public_key/include/public_key.hrl").
+
+-export([make_cert/1, gen_rsa/1, verify_signature/3, write_pem/3]).
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% @doc Create and return a der encoded certificate
+%% Option Default
+%% -------------------------------------------------------
+%% digest sha1
+%% validity {date(), date() + week()}
+%% version 3
+%% subject [] list of the following content
+%% {name, Name}
+%% {email, Email}
+%% {city, City}
+%% {state, State}
+%% {org, Org}
+%% {org_unit, OrgUnit}
+%% {country, Country}
+%% {serial, Serial}
+%% {title, Title}
+%% {dnQualifer, DnQ}
+%% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created)
+%% (obs IssuerKey migth be {Key, Password}
+%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key
+%%
+%%
+%% (OBS: The generated keys are for testing only)
+%% @spec ([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()}
+%% @end
+%%--------------------------------------------------------------------
+
+make_cert(Opts) ->
+ SubjectPrivateKey = get_key(Opts),
+ {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts),
+ Cert = public_key:pkix_sign(TBSCert, IssuerKey),
+ true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok
+ {Cert, encode_key(SubjectPrivateKey)}.
+
+%%--------------------------------------------------------------------
+%% @doc Writes pem files in Dir with FileName ++ ".pem" and FileName ++ "_key.pem"
+%% @spec (::string(), ::string(), {Cert,Key}) -> ok
+%% @end
+%%--------------------------------------------------------------------
+write_pem(Dir, FileName, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) ->
+ ok = der_to_pem(filename:join(Dir, FileName ++ ".pem"),
+ [{'Certificate', Cert, not_encrypted}]),
+ ok = der_to_pem(filename:join(Dir, FileName ++ "_key.pem"), [Key]).
+
+%%--------------------------------------------------------------------
+%% @doc Creates a rsa key (OBS: for testing only)
+%% the size are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_rsa(Size) when is_integer(Size) ->
+ Key = gen_rsa2(Size),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% @doc Creates a dsa key (OBS: for testing only)
+%% the sizes are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
+ Key = gen_dsa2(LSize, NSize),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% @doc Verifies cert signatures
+%% @spec (::binary(), ::tuple()) -> ::boolean()
+%% @end
+%%--------------------------------------------------------------------
+verify_signature(DerEncodedCert, DerKey, _KeyParams) ->
+ Key = decode_key(DerKey),
+ case Key of
+ #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} ->
+ public_key:pkix_verify(DerEncodedCert,
+ #'RSAPublicKey'{modulus=Mod, publicExponent=Exp});
+ #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} ->
+ public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}})
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_key(Opts) ->
+ case proplists:get_value(key, Opts) of
+ undefined -> make_key(rsa, Opts);
+ rsa -> make_key(rsa, Opts);
+ dsa -> make_key(dsa, Opts);
+ Key ->
+ Password = proplists:get_value(password, Opts, no_passwd),
+ decode_key(Key, Password)
+ end.
+
+decode_key({Key, Pw}) ->
+ decode_key(Key, Pw);
+decode_key(Key) ->
+ decode_key(Key, no_passwd).
+
+
+decode_key(#'RSAPublicKey'{} = Key,_) ->
+ Key;
+decode_key(#'RSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(#'DSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(PemEntry = {_,_,_}, Pw) ->
+ public_key:pem_entry_decode(PemEntry, Pw);
+decode_key(PemBin, Pw) ->
+ [KeyInfo] = public_key:pem_decode(PemBin),
+ decode_key(KeyInfo, Pw).
+
+encode_key(Key = #'RSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
+ {'RSAPrivateKey', list_to_binary(Der), not_encrypted};
+encode_key(Key = #'DSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
+ {'DSAPrivateKey', list_to_binary(Der), not_encrypted}.
+
+make_tbs(SubjectKey, Opts) ->
+ Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))),
+
+ IssuerProp = proplists:get_value(issuer, Opts, true),
+ {Issuer, IssuerKey} = issuer(IssuerProp, Opts, SubjectKey),
+
+ {Algo, Parameters} = sign_algorithm(IssuerKey, Opts),
+
+ SignAlgo = #'SignatureAlgorithm'{algorithm = Algo,
+ parameters = Parameters},
+ Subject = case IssuerProp of
+ true -> %% Is a Root Ca
+ Issuer;
+ _ ->
+ subject(proplists:get_value(subject, Opts),false)
+ end,
+
+ {#'OTPTBSCertificate'{serialNumber = trunc(random:uniform()*100000000)*10000 + 1,
+ signature = SignAlgo,
+ issuer = Issuer,
+ validity = validity(Opts),
+ subject = Subject,
+ subjectPublicKeyInfo = publickey(SubjectKey),
+ version = Version,
+ extensions = extensions(Opts)
+ }, IssuerKey}.
+
+issuer(true, Opts, SubjectKey) ->
+ %% Self signed
+ {subject(proplists:get_value(subject, Opts), true), SubjectKey};
+issuer({Issuer, IssuerKey}, _Opts, _SubjectKey) when is_binary(Issuer) ->
+ {issuer_der(Issuer), decode_key(IssuerKey)};
+issuer({File, IssuerKey}, _Opts, _SubjectKey) when is_list(File) ->
+ {ok, [{cert, Cert, _}|_]} = public_key:pem_to_der(File),
+ {issuer_der(Cert), decode_key(IssuerKey)}.
+
+issuer_der(Issuer) ->
+ Decoded = public_key:pkix_decode_cert(Issuer, otp),
+ #'OTPCertificate'{tbsCertificate=Tbs} = Decoded,
+ #'OTPTBSCertificate'{subject=Subject} = Tbs,
+ Subject.
+
+subject(undefined, IsRootCA) ->
+ User = if IsRootCA -> "RootCA"; true -> os:getenv("USER") end,
+ Opts = [{email, User ++ "@erlang.org"},
+ {name, User},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}],
+ subject(Opts);
+subject(Opts, _) ->
+ subject(Opts).
+
+subject(SubjectOpts) when is_list(SubjectOpts) ->
+ Encode = fun(Opt) ->
+ {Type,Value} = subject_enc(Opt),
+ [#'AttributeTypeAndValue'{type=Type, value=Value}]
+ end,
+ {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}.
+
+%% Fill in the blanks
+subject_enc({name, Name}) -> {?'id-at-commonName', {printableString, Name}};
+subject_enc({email, Email}) -> {?'id-emailAddress', Email};
+subject_enc({city, City}) -> {?'id-at-localityName', {printableString, City}};
+subject_enc({state, State}) -> {?'id-at-stateOrProvinceName', {printableString, State}};
+subject_enc({org, Org}) -> {?'id-at-organizationName', {printableString, Org}};
+subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}};
+subject_enc({country, Country}) -> {?'id-at-countryName', Country};
+subject_enc({serial, Serial}) -> {?'id-at-serialNumber', Serial};
+subject_enc({title, Title}) -> {?'id-at-title', {printableString, Title}};
+subject_enc({dnQualifer, DnQ}) -> {?'id-at-dnQualifier', DnQ};
+subject_enc(Other) -> Other.
+
+
+extensions(Opts) ->
+ case proplists:get_value(extensions, Opts, []) of
+ false ->
+ asn1_NOVALUE;
+ Exts ->
+ lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)])
+ end.
+
+default_extensions(Exts) ->
+ Def = [{key_usage,undefined},
+ {subject_altname, undefined},
+ {issuer_altname, undefined},
+ {basic_constraints, default},
+ {name_constraints, undefined},
+ {policy_constraints, undefined},
+ {ext_key_usage, undefined},
+ {inhibit_any, undefined},
+ {auth_key_id, undefined},
+ {subject_key_id, undefined},
+ {policy_mapping, undefined}],
+ Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end,
+ Exts ++ lists:foldl(Filter, Def, Exts).
+
+extension({_, undefined}) -> [];
+extension({basic_constraints, Data}) ->
+ case Data of
+ default ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true},
+ critical=true};
+ false ->
+ [];
+ Len when is_integer(Len) ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len},
+ critical=true};
+ _ ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = Data}
+ end;
+extension({Id, Data, Critical}) ->
+ #'Extension'{extnID = Id, extnValue = Data, critical = Critical}.
+
+
+publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) ->
+ Public = #'RSAPublicKey'{modulus=N, publicExponent=E},
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo,
+ subjectPublicKey = Public};
+publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) ->
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa',
+ parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}.
+
+validity(Opts) ->
+ DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1),
+ DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7),
+ {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}),
+ Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end,
+ #'Validity'{notBefore={generalTime, Format(DefFrom)},
+ notAfter ={generalTime, Format(DefTo)}}.
+
+sign_algorithm(#'RSAPrivateKey'{}, Opts) ->
+ Type = case proplists:get_value(digest, Opts, sha1) of
+ sha1 -> ?'sha1WithRSAEncryption';
+ sha512 -> ?'sha512WithRSAEncryption';
+ sha384 -> ?'sha384WithRSAEncryption';
+ sha256 -> ?'sha256WithRSAEncryption';
+ md5 -> ?'md5WithRSAEncryption';
+ md2 -> ?'md2WithRSAEncryption'
+ end,
+ {Type, 'NULL'};
+sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) ->
+ {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}.
+
+make_key(rsa, _Opts) ->
+ %% (OBS: for testing only)
+ gen_rsa2(64);
+make_key(dsa, _Opts) ->
+ gen_dsa2(128, 20). %% Bytes i.e. {1024, 160}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% RSA key generation (OBS: for testing only)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53,
+ 47,43,41,37,31,29,23,19,17,13,11,7,5,3]).
+
+gen_rsa2(Size) ->
+ P = prime(Size),
+ Q = prime(Size),
+ N = P*Q,
+ Tot = (P - 1) * (Q - 1),
+ [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES),
+ {D1,D2} = extended_gcd(E, Tot),
+ D = erlang:max(D1,D2),
+ case D < E of
+ true ->
+ gen_rsa2(Size);
+ false ->
+ {Co1,Co2} = extended_gcd(Q, P),
+ Co = erlang:max(Co1,Co2),
+ #'RSAPrivateKey'{version = 'two-prime',
+ modulus = N,
+ publicExponent = E,
+ privateExponent = D,
+ prime1 = P,
+ prime2 = Q,
+ exponent1 = D rem (P-1),
+ exponent2 = D rem (Q-1),
+ coefficient = Co
+ }
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% DSA key generation (OBS: for testing only)
+%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
+%% and the fips_186-3.pdf
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+gen_dsa2(LSize, NSize) ->
+ Q = prime(NSize), %% Choose N-bit prime Q
+ X0 = prime(LSize),
+ P0 = prime((LSize div 2) +1),
+
+ %% Choose L-bit prime modulus P such that p–1 is a multiple of q.
+ case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
+ error ->
+ gen_dsa2(LSize, NSize);
+ P ->
+ G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
+ %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used.
+
+ X = prime(20), %% Choose x by some random method, where 0 < x < q.
+ Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p.
+
+ #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X}
+ end.
+
+%% See fips_186-3.pdf
+dsa_search(T, P0, Q, Iter) when Iter > 0 ->
+ P = 2*T*Q*P0 + 1,
+ case is_prime(crypto:mpint(P), 50) of
+ true -> P;
+ false -> dsa_search(T+1, P0, Q, Iter-1)
+ end;
+dsa_search(_,_,_,_) ->
+ error.
+
+
+%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+prime(ByteSize) ->
+ Rand = odd_rand(ByteSize),
+ crypto:erlint(prime_odd(Rand, 0)).
+
+prime_odd(Rand, N) ->
+ case is_prime(Rand, 50) of
+ true ->
+ Rand;
+ false ->
+ NotPrime = crypto:erlint(Rand),
+ prime_odd(crypto:mpint(NotPrime+2), N+1)
+ end.
+
+%% see http://en.wikipedia.org/wiki/Fermat_primality_test
+is_prime(_, 0) -> true;
+is_prime(Candidate, Test) ->
+ CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate),
+ case crypto:mod_exp(CoPrime, Candidate, Candidate) of
+ CoPrime -> is_prime(Candidate, Test-1);
+ _ -> false
+ end.
+
+odd_rand(Size) ->
+ Min = 1 bsl (Size*8-1),
+ Max = (1 bsl (Size*8))-1,
+ odd_rand(crypto:mpint(Min), crypto:mpint(Max)).
+
+odd_rand(Min,Max) ->
+ Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max),
+ BitSkip = (Sz+4)*8-1,
+ case Rand of
+ Odd = <<_:BitSkip, 1:1>> -> Odd;
+ Even = <<_:BitSkip, 0:1>> ->
+ crypto:mpint(crypto:erlint(Even)+1)
+ end.
+
+extended_gcd(A, B) ->
+ case A rem B of
+ 0 ->
+ {0, 1};
+ N ->
+ {X, Y} = extended_gcd(B, N),
+ {Y, X-Y*(A div B)}
+ end.
+
+pem_to_der(File) ->
+ {ok, PemBin} = file:read_file(File),
+ public_key:pem_decode(PemBin).
+
+der_to_pem(File, Entries) ->
+ PemBin = public_key:pem_encode(Entries),
+ file:write_file(File, PemBin).
diff --git a/lib/public_key/test/pkits_SUITE.erl b/lib/public_key/test/pkits_SUITE.erl
index 5d58b39e26..a325a975e9 100644
--- a/lib/public_key/test/pkits_SUITE.erl
+++ b/lib/public_key/test/pkits_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. 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
@@ -25,8 +25,7 @@
-compile(export_all).
-%%-include_lib("public_key/include/public_key.hrl").
--include("public_key.hrl").
+-include_lib("public_key/include/public_key.hrl").
-define(error(Format,Args), error(Format,Args,?FILE,?LINE)).
-define(warning(Format,Args), warning(Format,Args,?FILE,?LINE)).
@@ -42,133 +41,772 @@
-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}).
%%
-all(doc) ->
- ["PKITS tests for RFC3280 compliance"];
-all(suite) ->
- [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].
-
-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()).
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{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.
+
+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}}]).
+
+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() ->
- catch crypto:start(),
- 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 ->
@@ -183,13 +821,317 @@ 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]),
- {ok, Ders} = public_key:pem_to_der(File),
- %% io:format("Ders ~p ~n",[length(Ders)]),
- [Cert || {cert,Cert,not_encrypted} <- Ders].
+ Ders = erl_make_certs:pem_to_der(File),
+ [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").
@@ -208,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 ->
+ do_sort_chain([],TA,[First|Try],Found, Chap)
+ end;
+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 ->
- sort_chain(Certs,TA,[First|Try],Found)
+ do_sort_chain(Certs,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) ->
+%% 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.
@@ -427,178 +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.
-
-fin_per_testcase(_Func, Config) ->
- %% Nodes = select_nodes(all, Config, ?FILE, ?LINE),
- %% rpc:multicall(Nodes, mnesia, lkill, []),
- Config.
-
-init_per_suite(Config) ->
- crypto:start(),
- Config.
-
-end_per_suite(_Config) ->
- crypto:stop().
-
-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.cover b/lib/public_key/test/public_key.cover
new file mode 100644
index 0000000000..ec00814578
--- /dev/null
+++ b/lib/public_key/test/public_key.cover
@@ -0,0 +1,4 @@
+{incl_app,public_key,details}.
+
+
+{excl_mods, public_key, ['OTP-PUB-KEY']}.
diff --git a/lib/public_key/test/public_key.spec b/lib/public_key/test/public_key.spec
index dee9ad44ed..1749822c2d 100644
--- a/lib/public_key/test/public_key.spec
+++ b/lib/public_key/test/public_key.spec
@@ -1,2 +1 @@
-{topcase, {dir, "../public_key_test"}}.
-
+{suites,"../public_key_test",all}.
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index 8cc36e490d..b11e4d092a 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.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
@@ -23,10 +23,10 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("test_server/include/test_server_line.hrl").
--include("public_key.hrl").
+-include_lib("public_key/include/public_key.hrl").
-define(TIMEOUT, 120000). % 2 min
@@ -41,9 +41,12 @@
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- crypto:start(),
- Config.
-
+ try crypto:start() of
+ ok ->
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
%%--------------------------------------------------------------------
%% Function: end_per_suite(Config) -> _
%% Config - [tuple()]
@@ -51,7 +54,7 @@ init_per_suite(Config) ->
%% Description: Cleanup after the whole suite
%%--------------------------------------------------------------------
end_per_suite(_Config) ->
- crypto:stop().
+ application:stop(crypto).
%%--------------------------------------------------------------------
%% Function: init_per_testcase(TestCase, Config) -> Config
@@ -96,21 +99,34 @@ end_per_testcase(_TestCase, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test the public_key rsa functionality"];
-
-all(suite) ->
- [app,
- pem_to_der,
- decode_private_key
-%% encrypt_decrypt,
-%% rsa_verify
-%% dsa_verify_sign,
-%% pkix_encode_decode,
-%% pkix_verify_sign,
-%% pkix_path_validation
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [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.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%% Test cases starts here.
%%--------------------------------------------------------------------
@@ -118,144 +134,606 @@ app(doc) ->
"Test that the public_key app file is ok";
app(suite) ->
[];
-app(Config) when list(Config) ->
+app(Config) when is_list(Config) ->
ok = test_server:app_test(public_key).
-pem_to_der(doc) ->
- ["Check that supported PEM files are decoded into the expected entry type"];
-pem_to_der(suite) ->
+%%--------------------------------------------------------------------
+
+dsa_pem(doc) ->
+ [""];
+dsa_pem(suite) ->
[];
-pem_to_der(Config) when is_list(Config) ->
+dsa_pem(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
- {ok,[{dsa_private_key, _, not_encrypted}]} =
- public_key:pem_to_der(filename:join(Datadir, "dsa.pem")),
- {ok,[{rsa_private_key, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "client_key.pem")),
- {ok,[{rsa_private_key, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "rsa.pem")),
- {ok,[{rsa_private_key, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "rsa.pem"), "abcd1234"),
- {ok, Bin0} = file:read_file(filename:join(Datadir, "rsa.pem")),
- {ok, [{rsa_private_key, _, _}]} = public_key:pem_to_der(Bin0, "abcd1234"),
-
- {ok,[{dh_params, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "dh.pem")),
- {ok,[{cert, _, not_encrypted}]} =
- public_key:pem_to_der(filename:join(Datadir, "client_cert.pem")),
- {ok,[{cert_req, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "req.pem")),
- {ok,[{cert, _, _}, {cert, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "cacerts.pem")),
-
- {ok, Bin1} = file:read_file(filename:join(Datadir, "cacerts.pem")),
- {ok, [{cert, _, _}, {cert, _, _}]} = public_key:pem_to_der(Bin1),
+
+ [{'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")),
+ [{'SubjectPublicKeyInfo', _, _} = PubEntry0] =
+ public_key:pem_decode(DSAPubPem),
+ DSAPubKey = public_key:pem_entry_decode(PubEntry0),
+ true = check_entry_type(DSAPubKey, 'DSAPublicKey'),
+ PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', DSAPubKey),
+ DSAPubPemNoEndNewLines = strip_ending_newlines(DSAPubPem),
+ 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),
- ok.
+ [{'RSAPrivateKey', _, {_,_}} = Entry1] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "rsa.pem")),
+
+ true = check_entry_type(public_key:pem_entry_decode(Entry1, "abcd1234"),
+ 'RSAPrivateKey'),
+
+ {ok, RSAPubPem} = file:read_file(filename:join(Datadir, "rsa_pub.pem")),
+ [{'SubjectPublicKeyInfo', _, _} = PubEntry0] =
+ public_key:pem_decode(RSAPubPem),
+ RSAPubKey = public_key:pem_entry_decode(PubEntry0),
+ true = check_entry_type(RSAPubKey, 'RSAPublicKey'),
+ PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey),
+ RSAPubPemNoEndNewLines = strip_ending_newlines(RSAPubPem),
+ RSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])),
+
+ {ok, RSARawPem} = file:read_file(filename:join(Datadir, "rsa_pub_key.pem")),
+ [{'RSAPublicKey', _, _} = PubEntry1] =
+ public_key:pem_decode(RSARawPem),
+ RSAPubKey = public_key:pem_entry_decode(PubEntry1),
+ RSARawPemNoEndNewLines = strip_ending_newlines(RSARawPem),
+ RSARawPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry1])).
+
%%--------------------------------------------------------------------
-decode_private_key(doc) ->
- ["Check that private keys are decode to the expected key type."];
-decode_private_key(suite) ->
+
+encrypted_pem(doc) ->
+ [""];
+encrypted_pem(suite) ->
[];
-decode_private_key(Config) when is_list(Config) ->
+encrypted_pem(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
- {ok,[DsaKey = {dsa_private_key, _DsaKey, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "dsa.pem")),
- {ok,[RsaKey = {rsa_private_key, _RsaKey,_}]} =
- public_key:pem_to_der(filename:join(Datadir, "client_key.pem")),
- {ok,[ProtectedRsaKey1 = {rsa_private_key, _ProtectedRsaKey1,_}]} =
- public_key:pem_to_der(filename:join(Datadir, "rsa.pem"), "abcd1234"),
- {ok,[ProtectedRsaKey2 = {rsa_private_key, _ProtectedRsaKey2,_}]} =
- public_key:pem_to_der(filename:join(Datadir, "rsa.pem")),
- {ok, #'DSAPrivateKey'{}} = public_key:decode_private_key(DsaKey),
- {ok, #'RSAPrivateKey'{}} = public_key:decode_private_key(RsaKey),
- {ok, #'RSAPrivateKey'{}} = public_key:decode_private_key(ProtectedRsaKey1),
- {ok, #'RSAPrivateKey'{}} = public_key:decode_private_key(ProtectedRsaKey2, "abcd1234"),
- ok.
+ [{'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),
+ Entry0 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey,
+ {{"DES-EDE3-CBC", Salt0}, "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),
+
+ Salt1 = crypto:rand_bytes(8),
+ 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').
+
+%%--------------------------------------------------------------------
+
+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"), [Entry]),
+
+ DHParameter = public_key:der_decode('DHParameter', DerDH),
+ DHParameter = public_key:pem_entry_decode(Entry),
+
+ 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")),
+
+ Cert = public_key:der_decode('Certificate', DerCert),
+ Cert = public_key:pem_entry_decode(Entry7),
+
+ 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")).
+
+%%--------------------------------------------------------------------
+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) ->
[""];
encrypt_decrypt(suite) ->
[];
encrypt_decrypt(Config) when is_list(Config) ->
- RSAPrivateKey = #'RSAPrivateKey'{publicExponent = 17,
- modulus = 3233,
- privateExponent = 2753,
- prime1 = 61,
- prime2 = 53,
- version = 'two-prime'},
- Msg = <<0,123>>,
- {ok, Encrypted} = public_key:encrypt(Msg, RSAPrivateKey, [{block_type, 2}]),
- test_server:format("Expected 855, Encrypted ~p ~n", [Encrypted]),
+ {PrivateKey, _DerKey} = erl_make_certs:gen_rsa(64),
+ #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} = PrivateKey,
+ PublicKey = #'RSAPublicKey'{modulus=Mod, publicExponent=Exp},
+ Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")),
+ RsaEncrypted = public_key:encrypt_private(Msg, PrivateKey),
+ Msg = public_key:decrypt_public(RsaEncrypted, PublicKey),
+ Msg = public_key:decrypt_public(RsaEncrypted, PrivateKey),
+ RsaEncrypted2 = public_key:encrypt_public(Msg, PublicKey),
+ RsaEncrypted3 = public_key:encrypt_public(Msg, PrivateKey),
+ Msg = public_key:decrypt_private(RsaEncrypted2, PrivateKey),
+ Msg = public_key:decrypt_private(RsaEncrypted3, PrivateKey),
+
ok.
+
+%%--------------------------------------------------------------------
+rsa_sign_verify(doc) ->
+ ["Checks that we can sign and verify rsa signatures."];
+rsa_sign_verify(suite) ->
+ [];
+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),
+ PublicRSA = #'RSAPublicKey'{modulus=Mod, publicExponent=Exp},
+ true = public_key:pkix_verify(Cert1, PublicRSA),
+
+ 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).
+%%--------------------------------------------------------------------
+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},
+ true = public_key:verify(Msg, sha, DSASign, {DSAPublicKey, DSAParams}),
+ false = public_key:verify(<<1:8, Msg/binary>>, sha, DSASign,
+ {DSAPublicKey, DSAParams}),
+ false = public_key:verify(Msg, sha, <<1:8, DSASign/binary>>,
+ {DSAPublicKey, DSAParams}),
+
+ Digest = crypto:sha(Msg),
+ DigestSign = public_key:sign(Digest, none, DSAPrivateKey),
+ true = public_key:verify(Digest, none, DigestSign, {DSAPublicKey, DSAParams}),
+ <<_:8, RestDigest/binary>> = Digest,
+ false = public_key:verify(<<1:8, RestDigest/binary>>, none, DigestSign,
+ {DSAPublicKey, DSAParams}),
+ false = public_key:verify(Digest, none, <<1:8, DigestSign/binary>>,
+ {DSAPublicKey, DSAParams}).
+%%--------------------------------------------------------------------
+pkix(doc) ->
+ "Misc pkix tests not covered elsewhere";
+pkix(suite) ->
+ [];
+pkix(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ Certs0 = erl_make_certs:pem_to_der(filename:join(Datadir, "cacerts.pem")),
+ Certs1 = erl_make_certs:pem_to_der(filename:join(Datadir, "client_cert.pem")),
+ TestTransform = fun({'Certificate', CertDer, not_encrypted}) ->
+ PlainCert = public_key:pkix_decode_cert(CertDer, plain),
+ OtpCert = public_key:pkix_decode_cert(CertDer, otp),
+ CertDer =
+ public_key:pkix_encode('OTPCertificate', OtpCert, otp),
+ CertDer =
+ public_key:pkix_encode('Certificate', PlainCert, plain),
+ OTPTBS = OtpCert#'OTPCertificate'.tbsCertificate,
+ OTPSubj = OTPTBS#'OTPTBSCertificate'.subject,
+ DNEncoded = public_key:pkix_encode('Name', OTPSubj, otp),
+ PlainTBS = PlainCert#'Certificate'.tbsCertificate,
+ Subj2 = PlainTBS#'TBSCertificate'.subject,
+ DNEncoded = public_key:pkix_encode('Name', Subj2, plain),
+ false = public_key:pkix_is_fixed_dh_cert(CertDer)
+ end,
+ [TestTransform(Cert) || Cert <- Certs0 ++ Certs1],
+ true = public_key:pkix_is_self_signed(element(2,hd(Certs0))),
+ false = public_key:pkix_is_self_signed(element(2,hd(Certs1))),
-%% Datadir = ?config(data_dir, Config),
-%% {ok,[{rsa_private_key, EncKey}]} =
-%% public_key:pem_to_der(filename:join(Datadir, "server_key.pem")),
-%% {ok, Key} = public_key:decode_private_key(EncKey, rsa),
-%% RSAPublicKey = #'RSAPublicKey'{publicExponent =
-%% Key#'RSAPrivateKey'.publicExponent,
-%% modulus = Key#'RSAPrivateKey'.modulus},
-%% {ok, Msg} = file:read_file(filename:join(Datadir, "msg.txt")),
-%% Hash = crypto:sha(Msg),
-%% {ok, Encrypted} = public_key:encrypt(Hash, Key, [{block_type, 2}]),
-%% test_server:format("Encrypted ~p", [Encrypted]),
-%% {ok, Decrypted} = public_key:decrypt(Encrypted,
-%% RSAPublicKey, [{block_type, 1}]),
-%% test_server:format("Encrypted ~p", [Decrypted]),
-%% true = Encrypted == Decrypted.
-
+ CaIds = [element(2, public_key:pkix_issuer_id(Cert, self)) ||
+ {'Certificate', Cert, _} <- Certs0],
+ {ok, IssuerId = {_, _IssuerName}} =
+ public_key:pkix_issuer_id(element(2,hd(Certs1)), other),
+
+ true = lists:member(IssuerId, CaIds),
+
+ %% Should be normalized allready
+ TestStr = {rdnSequence,
+ [[{'AttributeTypeAndValue', {2,5,4,3},{printableString,"ERLANGCA"}}],
+ [{'AttributeTypeAndValue', {2,5,4,3},{printableString," erlang ca "}}]]},
+ VerifyStr = {rdnSequence,
+ [[{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlang ca"}}],
+ [{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlangca"}}]]},
+ VerifyStr = public_key:pkix_normalize_name(TestStr),
+
+ ok.
%%--------------------------------------------------------------------
-rsa_verify(doc) ->
- ["Cheks that we can verify an rsa signature."];
-rsa_verify(suite) ->
+pkix_path_validation(doc) ->
+ "Misc pkix tests not covered elsewhere";
+pkix_path_validation(suite) ->
[];
-rsa_verify(Config) when is_list(Config) ->
- Datadir = ?config(data_dir, Config),
+pkix_path_validation(Config) when is_list(Config) ->
+ CaK = {Trusted,_} =
+ erl_make_certs:make_cert([{key, dsa},
+ {subject, [
+ {name, "Public Key"},
+ {?'id-at-name', {printableString, "public_key"}},
+ {?'id-at-pseudonym', {printableString, "pubkey"}},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}
+ ]}
+ ]),
+ ok = erl_make_certs:write_pem("./", "public_key_cacert", CaK),
+
+ CertK1 = {Cert1, _} = erl_make_certs:make_cert([{issuer, CaK}]),
+ CertK2 = {Cert2,_} = erl_make_certs:make_cert([{issuer, CertK1},
+ {digest, md5}, {extensions, false}]),
+ ok = erl_make_certs:write_pem("./", "public_key_cert", CertK2),
- {ok,[{cert, DerCert}]} =
- public_key:pem_to_der(filename:join(Datadir, "server_cert.pem")),
+ {ok, _} = public_key:pkix_path_validation(Trusted, [Cert1], []),
- {ok, OTPCert} = public_key:pkix_decode_cert(DerCert, otp),
+ {error, {bad_cert,invalid_issuer}} =
+ public_key:pkix_path_validation(Trusted, [Cert2], []),
- {0, Signature} = OTPCert#'Certificate'.signature,
- TBSCert = OTPCert#'Certificate'.tbsCertificate,
+ {ok, _} = public_key:pkix_path_validation(Trusted, [Cert1, Cert2], []),
+ {error, issuer_not_found} = public_key:pkix_issuer_id(Cert2, other),
- #'TBSCertificate'{subjectPublicKeyInfo = Info} = TBSCert,
-
- #'SubjectPublicKeyInfo'{subjectPublicKey = RSAPublicKey} = Info,
-
- EncTBSCert = encoded_tbs_cert(DerCert),
- Digest = crypto:sha(EncTBSCert),
-
- public_key:verify_signature(Digest, Signature, RSAPublicKey).
-
-
-%% Signature is generated in the following way (in datadir):
-%% openssl dgst -sha1 -binary -out rsa_signature -sign server_key.pem msg.txt
-%%{ok, Signature} = file:read_file(filename:join(Datadir, "rsa_signature")),
-%%{ok, Signature} = file:read_file(filename:join(Datadir, "rsa_signature")),
-%% {ok, Msg} = file:read_file(filename:join(Datadir, "msg.txt")),
-%% Digest = crypto:sha(Msg),
-%% {ok,[{rsa_private_key, EncKey}]} =
-%% public_key:pem_to_der(filename:join(Datadir, "server_key.pem")),
-%% {ok, Key} = public_key:decode_private_key(EncKey, rsa),
-%% RSAPublicKey = #'RSAPublicKey'{publicExponent =
-%% Key#'RSAPrivateKey'.publicExponent,
-%% modulus = Key#'RSAPrivateKey'.modulus},
-
-encoded_tbs_cert(Cert) ->
- {ok, PKIXCert} =
- 'OTP-PUB-KEY':decode_TBSCert_exclusive(Cert),
- {'Certificate',
- {'Certificate_tbsCertificate', EncodedTBSCert}, _, _} = PKIXCert,
- EncodedTBSCert.
+ CertK3 = {Cert3,_} = erl_make_certs:make_cert([{issuer, CertK1},
+ {extensions, [{basic_constraints, false}]}]),
+ {Cert4,_} = erl_make_certs:make_cert([{issuer, CertK3}]),
+ {error, {bad_cert,missing_basic_constraint}} =
+ public_key:pkix_path_validation(Trusted, [Cert1, Cert3,Cert4], []),
+
+ VerifyFunAndState0 = {fun(_,{bad_cert, missing_basic_constraint}, UserState) ->
+ {valid, UserState};
+ (_,{bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []},
+ {ok, _} =
+ public_key:pkix_path_validation(Trusted, [Cert1, Cert3,Cert4],
+ [{verify_fun, VerifyFunAndState0}]),
+
+ {error, {bad_cert, unknown_ca}} =
+ public_key:pkix_path_validation(unknown_ca, [Cert1, Cert3, Cert4], []),
+
+ VerifyFunAndState1 =
+ {fun(_,{bad_cert, unknown_ca}, UserState) ->
+ {valid, UserState};
+ (_,{bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState}
+ end, []},
+
+ {ok, _} =
+ public_key:pkix_path_validation(unknown_ca, [Cert1], [{verify_fun,
+ VerifyFunAndState1}]),
+ ok.
+
+%%--------------------------------------------------------------------
+deprecated(doc) ->
+ ["Check deprecated functions."];
+deprecated(suite) ->
+ [];
+deprecated(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ {ok, [DsaKey = {'DSAPrivateKey', _DsaKey, _}]} =
+ public_key:pem_to_der(filename:join(Datadir, "dsa.pem")),
+ {ok, [RsaKey = {'RSAPrivateKey', _RsaKey,_}]} =
+ public_key:pem_to_der(filename:join(Datadir, "client_key.pem")),
+ {ok, [ProtectedRsaKey = {'RSAPrivateKey', _ProtectedRsaKey,_}]} =
+ public_key:pem_to_der(filename:join(Datadir, "rsa.pem")),
+
+ {ok, #'DSAPrivateKey'{}} = public_key:decode_private_key(DsaKey),
+ {ok, #'RSAPrivateKey'{}} = public_key:decode_private_key(RsaKey),
+ {ok, #'RSAPrivateKey'{}} = public_key:decode_private_key(ProtectedRsaKey, "abcd1234"),
+ ok.
+
+%%--------------------------------------------------------------------
+
+check_entry_type(#'DSAPrivateKey'{}, 'DSAPrivateKey') ->
+ true;
+check_entry_type(#'RSAPrivateKey'{}, 'RSAPrivateKey') ->
+ true;
+check_entry_type(#'RSAPublicKey'{}, 'RSAPublicKey') ->
+ true;
+check_entry_type({_Int, #'Dss-Parms'{}}, 'DSAPublicKey') when is_integer(_Int) ->
+ true;
+check_entry_type(#'DHParameter'{}, 'DHParameter') ->
+ true;
+check_entry_type(#'Certificate'{}, 'Certificate') ->
+ true;
+check_entry_type(_,_) ->
+ false.
+strip_ending_newlines(Bin) ->
+ string:strip(binary_to_list(Bin), right, 10).
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/dsa_pub.pem b/lib/public_key/test/public_key_SUITE_data/dsa_pub.pem
new file mode 100644
index 0000000000..d3635e5b20
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/dsa_pub.pem
@@ -0,0 +1,12 @@
+-----BEGIN PUBLIC KEY-----
+MIIBtzCCASwGByqGSM44BAEwggEfAoGBALez5tklY5CdFeTMos899pA6i4u4uCts
+zgBzrdBk6cl5FVqzdzWMGTQiynnTpGsrOESinzP06Ip+pG15We2OORwgvCxD/W95
+aCiN0/+MdiXqlsmboBARMzsa+SmBENN3gF/+tuuEAFzOXU1q2cmEywRLyfbM2KIB
+VE/TChWYw2eRAhUA1R64VvcQ90XA8SOKVDmMA0dBzukCgYEAlLMYP0pbgBlgHQVO
+3/avAHlWNrIq52Lxk7SdPJWgMvPjTK9Z6sv88kxsCcydtjvO439j1yqcwk50GQc+
+86ktBWWz93/HkIdnFyqafef4mmWvm2Uq6ClQKS+A0Asfaj8Mys+HUMiI+qsfdjRb
+yIpwb7MX1nsVdsKzALnZNMW27A0DgYQAAoGAfEIAb3mLjtFfiF/tsZb4/DGHdWSb
+6Ir0hFkoBUZ9ymBO70wlfZVSQGs240kZtOMpAOpJL1Dy8oH6PUQ+JyacwZIo8fdq
+19/Kwm6CPrpaEhzErmMvwT2CZJYZ+HOk55ljLkVCiyG7MzEj2+odLKym9yoQsbsJ
+olHzIRpkLk45y4c=
+-----END PUBLIC KEY-----
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/rsa_pub.pem b/lib/public_key/test/public_key_SUITE_data/rsa_pub.pem
new file mode 100644
index 0000000000..cbe81343f7
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/rsa_pub.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANRiyZg0uci74Nc6mnqZ8AoDl88aT7x6
+JA0MfgHIHzteEj7Qg+lE5QxMGAafurVE5vqoHkDfwk4uzzsCAJuz91MCAwEAAQ==
+-----END PUBLIC KEY-----
diff --git a/lib/public_key/test/public_key_SUITE_data/rsa_pub_key.pem b/lib/public_key/test/public_key_SUITE_data/rsa_pub_key.pem
new file mode 100644
index 0000000000..3b9d7568ff
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/rsa_pub_key.pem
@@ -0,0 +1,4 @@
+-----BEGIN RSA PUBLIC KEY-----
+MEgCQQDUYsmYNLnIu+DXOpp6mfAKA5fPGk+8eiQNDH4ByB87XhI+0IPpROUMTBgG
+n7q1ROb6qB5A38JOLs87AgCbs/dTAgMBAAE=
+-----END RSA PUBLIC KEY-----
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 da1465d538..66ac78a65d 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1,8 +1 @@
-PUBLIC_KEY_VSN = 0.6
-TICKETS = OTP-7046 \
- OTP-8553
-#TICKETS_o.5 = OTP-8372
-#TICKETS_0.4 = OTP-8250
-#TICKETS_0.3 = OTP-8100 OTP-8142
-#TICKETS_0.2 = OTP-7860
-#TICKETS_0.1 = OTP-7637 \ No newline at end of file
+PUBLIC_KEY_VSN = 0.13