From ca4d1197fdf66fda5241edd645c12d2451b67ec8 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Thu, 6 Oct 2011 17:37:02 +0200 Subject: Add PKCS-8 support to public_key --- lib/public_key/asn1/InformationFramework.asn1 | 682 +++++++++++++++++++++ lib/public_key/asn1/Makefile | 18 +- lib/public_key/asn1/PKCS-8.asn1 | 83 +++ lib/public_key/asn1/PKCS-FRAME.set.asn | 3 + lib/public_key/asn1/PKCS5v2-0.asn1 | 142 +++++ lib/public_key/include/public_key.hrl | 1 + lib/public_key/src/Makefile | 6 +- lib/public_key/src/pubkey_pbe.erl | 181 ++++++ lib/public_key/src/pubkey_pem.erl | 118 ++-- lib/public_key/src/public_key.app.src | 4 +- lib/public_key/src/public_key.erl | 72 ++- lib/public_key/test/Makefile | 3 +- lib/public_key/test/pbe_SUITE.erl | 251 ++++++++ .../test/pbe_SUITE_data/des_cbc_enc_key.pem | 11 + .../test/pbe_SUITE_data/des_ede3_cbc_enc_key.pem | 11 + .../test/pbe_SUITE_data/rc2_cbc_enc_key.pem | 12 + lib/public_key/test/public_key_SUITE.erl | 48 +- 17 files changed, 1537 insertions(+), 109 deletions(-) create mode 100644 lib/public_key/asn1/InformationFramework.asn1 create mode 100644 lib/public_key/asn1/PKCS-8.asn1 create mode 100644 lib/public_key/asn1/PKCS-FRAME.set.asn create mode 100644 lib/public_key/asn1/PKCS5v2-0.asn1 create mode 100644 lib/public_key/src/pubkey_pbe.erl create mode 100644 lib/public_key/test/pbe_SUITE.erl create mode 100644 lib/public_key/test/pbe_SUITE_data/des_cbc_enc_key.pem create mode 100644 lib/public_key/test/pbe_SUITE_data/des_ede3_cbc_enc_key.pem create mode 100644 lib/public_key/test/pbe_SUITE_data/rc2_cbc_enc_key.pem (limited to 'lib/public_key') diff --git a/lib/public_key/asn1/InformationFramework.asn1 b/lib/public_key/asn1/InformationFramework.asn1 new file mode 100644 index 0000000000..40fbd11a2a --- /dev/null +++ b/lib/public_key/asn1/InformationFramework.asn1 @@ -0,0 +1,682 @@ +InformationFramework {joint-iso-itu-t ds(5) module(1) informationFramework(1) + 6} DEFINITIONS ::= +BEGIN + +-- EXPORTS All +-- The types and values defined in this module are exported for use in the other ASN.1 modules contained +-- within the Directory Specifications, and for the use of other applications which will use them to access +-- Directory services. Other applications may use them for their own purposes, but this will not constrain +-- extensions and modifications needed to maintain or improve the Directory service. +IMPORTS + -- from ITU-T Rec. X.501 | ISO/IEC 9594-2 + directoryAbstractService, id-ar, id-at, id-mr, id-nf, id-oa, id-oc, + id-sc, selectedAttributeTypes, serviceAdministration + FROM UsefulDefinitions {joint-iso-itu-t ds(5) module(1) + usefulDefinitions(0) 6} + SearchRule + FROM ServiceAdministration serviceAdministration + -- from ITU-T Rec. X.511 | ISO/IEC 9594-3 + TypeAndContextAssertion + FROM DirectoryAbstractService directoryAbstractService + -- from ITU-T Rec. X.520 | ISO/IEC 9594-6 + booleanMatch, commonName, generalizedTimeMatch, generalizedTimeOrderingMatch, + integerFirstComponentMatch, integerMatch, integerOrderingMatch, + objectIdentifierFirstComponentMatch, UnboundedDirectoryString + FROM SelectedAttributeTypes selectedAttributeTypes; + +-- attribute data types +Attribute{ATTRIBUTE:SupportedAttributes} ::= SEQUENCE { + type ATTRIBUTE.&id({SupportedAttributes}), + values + SET SIZE (0..MAX) OF ATTRIBUTE.&Type({SupportedAttributes}{@type}), + valuesWithContext + SET SIZE (1..MAX) OF + SEQUENCE {value ATTRIBUTE.&Type({SupportedAttributes}{@type}), + contextList SET SIZE (1..MAX) OF Context} OPTIONAL +} + +AttributeType ::= ATTRIBUTE.&id + +AttributeValue ::= ATTRIBUTE.&Type + +Context ::= SEQUENCE { + contextType CONTEXT.&id({SupportedContexts}), + contextValues + SET SIZE (1..MAX) OF CONTEXT.&Type({SupportedContexts}{@contextType}), + fallback BOOLEAN DEFAULT FALSE +} + +AttributeValueAssertion ::= SEQUENCE { + type ATTRIBUTE.&id({SupportedAttributes}), + assertion + ATTRIBUTE.&equality-match.&AssertionType + ({SupportedAttributes}{@type}), + assertedContexts + CHOICE {allContexts [0] NULL, + selectedContexts [1] SET SIZE (1..MAX) OF ContextAssertion + } OPTIONAL +} + +ContextAssertion ::= SEQUENCE { + contextType CONTEXT.&id({SupportedContexts}), + contextValues + SET SIZE (1..MAX) OF + CONTEXT.&Assertion({SupportedContexts}{@contextType}) +} + +AttributeTypeAssertion ::= SEQUENCE { + type ATTRIBUTE.&id({SupportedAttributes}), + assertedContexts SEQUENCE SIZE (1..MAX) OF ContextAssertion OPTIONAL +} + +-- Definition of the following information object set is deferred, perhaps to standardized +-- profiles or to protocol implementation conformance statements. The set is required to +-- specify a table constraint on the values component of Attribute, the value component +-- of AttributeTypeAndValue, and the assertion component of AttributeValueAssertion. +SupportedAttributes ATTRIBUTE ::= + {objectClass | aliasedEntryName, ...} + +-- Definition of the following information object set is deferred, perhaps to standardized +-- profiles or to protocol implementation conformance statements. The set is required to +-- specify a table constraint on the context specifications +SupportedContexts CONTEXT ::= + {...} + +-- naming data types +Name ::= CHOICE { -- only one possibility for now --rdnSequence RDNSequence +} + +RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + +DistinguishedName ::= RDNSequence + +RelativeDistinguishedName ::= + SET SIZE (1..MAX) OF AttributeTypeAndDistinguishedValue + +AttributeTypeAndDistinguishedValue ::= SEQUENCE { + type ATTRIBUTE.&id({SupportedAttributes}), + value ATTRIBUTE.&Type({SupportedAttributes}{@type}), + primaryDistinguished BOOLEAN DEFAULT TRUE, + valuesWithContext + SET SIZE (1..MAX) OF + SEQUENCE {distingAttrValue + [0] ATTRIBUTE.&Type({SupportedAttributes}{@type}) + OPTIONAL, + contextList SET SIZE (1..MAX) OF Context} OPTIONAL +} + +-- subtree data types +SubtreeSpecification ::= SEQUENCE { + base [0] LocalName DEFAULT {}, + COMPONENTS OF ChopSpecification, + specificationFilter [4] Refinement OPTIONAL +} + +-- empty sequence specifies whole administrative area +LocalName ::= RDNSequence + +ChopSpecification ::= SEQUENCE { + specificExclusions + [1] SET SIZE (1..MAX) OF + CHOICE {chopBefore [0] LocalName, + chopAfter [1] LocalName} OPTIONAL, + minimum [2] BaseDistance DEFAULT 0, + maximum [3] BaseDistance OPTIONAL +} + +BaseDistance ::= INTEGER(0..MAX) + +Refinement ::= CHOICE { + item [0] OBJECT-CLASS.&id, + and [1] SET SIZE (1..MAX) OF Refinement, + or [2] SET SIZE (1..MAX) OF Refinement, + not [3] Refinement +} + +-- OBJECT-CLASS information object class specification +OBJECT-CLASS ::= CLASS { + &Superclasses OBJECT-CLASS OPTIONAL, + &kind ObjectClassKind DEFAULT structural, + &MandatoryAttributes ATTRIBUTE OPTIONAL, + &OptionalAttributes ATTRIBUTE OPTIONAL, + &id OBJECT IDENTIFIER UNIQUE +} +WITH SYNTAX { + [SUBCLASS OF &Superclasses] + [KIND &kind] + [MUST CONTAIN &MandatoryAttributes] + [MAY CONTAIN &OptionalAttributes] + ID &id +} + +ObjectClassKind ::= ENUMERATED {abstract(0), structural(1), auxiliary(2)} + +-- object classes +top OBJECT-CLASS ::= { + KIND abstract + MUST CONTAIN {objectClass} + ID id-oc-top +} + +alias OBJECT-CLASS ::= { + SUBCLASS OF {top} + MUST CONTAIN {aliasedEntryName} + ID id-oc-alias +} + +parent OBJECT-CLASS ::= {KIND abstract + ID id-oc-parent +} + +child OBJECT-CLASS ::= {KIND auxiliary + ID id-oc-child +} + +-- ATTRIBUTE information object class specification +ATTRIBUTE ::= CLASS { + &derivation ATTRIBUTE OPTIONAL, + &Type OPTIONAL, -- either &Type or &derivation required + &equality-match MATCHING-RULE OPTIONAL, + &ordering-match MATCHING-RULE OPTIONAL, + &substrings-match MATCHING-RULE OPTIONAL, + &single-valued BOOLEAN DEFAULT FALSE, + &collective BOOLEAN DEFAULT FALSE, + &dummy BOOLEAN DEFAULT FALSE, + -- operational extensions + &no-user-modification BOOLEAN DEFAULT FALSE, + &usage AttributeUsage DEFAULT userApplications, + &id OBJECT IDENTIFIER UNIQUE +} +WITH SYNTAX { + [SUBTYPE OF &derivation] + [WITH SYNTAX &Type] + [EQUALITY MATCHING RULE &equality-match] + [ORDERING MATCHING RULE &ordering-match] + [SUBSTRINGS MATCHING RULE &substrings-match] + [SINGLE VALUE &single-valued] + [COLLECTIVE &collective] + [DUMMY &dummy] + [NO USER MODIFICATION &no-user-modification] + [USAGE &usage] + ID &id +} + +AttributeUsage ::= ENUMERATED { + userApplications(0), directoryOperation(1), distributedOperation(2), + dSAOperation(3)} + +-- attributes +objectClass ATTRIBUTE ::= { + WITH SYNTAX OBJECT IDENTIFIER + EQUALITY MATCHING RULE objectIdentifierMatch + ID id-at-objectClass +} + +aliasedEntryName ATTRIBUTE ::= { + WITH SYNTAX DistinguishedName + EQUALITY MATCHING RULE distinguishedNameMatch + SINGLE VALUE TRUE + ID id-at-aliasedEntryName +} + +-- MATCHING-RULE information object class specification +MATCHING-RULE ::= CLASS { + &ParentMatchingRules MATCHING-RULE OPTIONAL, + &AssertionType OPTIONAL, + &uniqueMatchIndicator ATTRIBUTE OPTIONAL, + &id OBJECT IDENTIFIER UNIQUE +} +WITH SYNTAX { + [PARENT &ParentMatchingRules] + [SYNTAX &AssertionType] + [UNIQUE-MATCH-INDICATOR &uniqueMatchIndicator] + ID &id +} + +-- matching rules +objectIdentifierMatch MATCHING-RULE ::= { + SYNTAX OBJECT IDENTIFIER + ID id-mr-objectIdentifierMatch +} + +distinguishedNameMatch MATCHING-RULE ::= { + SYNTAX DistinguishedName + ID id-mr-distinguishedNameMatch +} + +MAPPING-BASED-MATCHING{SelectedBy, BOOLEAN:combinable, MappingResult, + OBJECT IDENTIFIER:matchingRule} ::= CLASS { + &selectBy SelectedBy OPTIONAL, + &ApplicableTo ATTRIBUTE, + &subtypesIncluded BOOLEAN DEFAULT TRUE, + &combinable BOOLEAN(combinable), + &mappingResults MappingResult OPTIONAL, + &userControl BOOLEAN DEFAULT FALSE, + &exclusive BOOLEAN DEFAULT TRUE, + &matching-rule MATCHING-RULE.&id(matchingRule), + &id OBJECT IDENTIFIER UNIQUE +} +WITH SYNTAX { + [SELECT BY &selectBy] + APPLICABLE TO &ApplicableTo + [SUBTYPES INCLUDED &subtypesIncluded] + COMBINABLE &combinable + [MAPPING RESULTS &mappingResults] + [USER CONTROL &userControl] + [EXCLUSIVE &exclusive] + MATCHING RULE &matching-rule + ID &id +} + +-- NAME-FORM information object class specification +NAME-FORM ::= CLASS { + &namedObjectClass OBJECT-CLASS, + &MandatoryAttributes ATTRIBUTE, + &OptionalAttributes ATTRIBUTE OPTIONAL, + &id OBJECT IDENTIFIER UNIQUE +} +WITH SYNTAX { + NAMES &namedObjectClass + WITH ATTRIBUTES &MandatoryAttributes + [AND OPTIONALLY &OptionalAttributes] + ID &id +} + +-- STRUCTURE-RULE class and DIT structure rule data types +DITStructureRule ::= SEQUENCE { + ruleIdentifier RuleIdentifier, + -- shall be unique within the scope of the subschema + nameForm NAME-FORM.&id, + superiorStructureRules SET SIZE (1..MAX) OF RuleIdentifier OPTIONAL +} + +RuleIdentifier ::= INTEGER + +STRUCTURE-RULE ::= CLASS { + &nameForm NAME-FORM, + &SuperiorStructureRules STRUCTURE-RULE OPTIONAL, + &id RuleIdentifier +} +WITH SYNTAX { + NAME FORM &nameForm + [SUPERIOR RULES &SuperiorStructureRules] + ID &id +} + +-- DIT content rule data type and CONTENT-RULE class +DITContentRule ::= SEQUENCE { + structuralObjectClass OBJECT-CLASS.&id, + auxiliaries SET SIZE (1..MAX) OF OBJECT-CLASS.&id OPTIONAL, + mandatory [1] SET SIZE (1..MAX) OF ATTRIBUTE.&id OPTIONAL, + optional [2] SET SIZE (1..MAX) OF ATTRIBUTE.&id OPTIONAL, + precluded [3] SET SIZE (1..MAX) OF ATTRIBUTE.&id OPTIONAL +} + +CONTENT-RULE ::= CLASS { + &structuralClass OBJECT-CLASS.&id UNIQUE, + &Auxiliaries OBJECT-CLASS OPTIONAL, + &Mandatory ATTRIBUTE OPTIONAL, + &Optional ATTRIBUTE OPTIONAL, + &Precluded ATTRIBUTE OPTIONAL +} +WITH SYNTAX { + STRUCTURAL OBJECT-CLASS &structuralClass + [AUXILIARY OBJECT-CLASSES &Auxiliaries] + [MUST CONTAIN &Mandatory] + [MAY CONTAIN &Optional] + [MUST-NOT CONTAIN &Precluded] +} + +CONTEXT ::= CLASS { + &Type , + &DefaultValue OPTIONAL, + &Assertion OPTIONAL, + &absentMatch BOOLEAN DEFAULT TRUE, + &id OBJECT IDENTIFIER UNIQUE +} +WITH SYNTAX { + WITH SYNTAX &Type + [DEFAULT-VALUE &DefaultValue] + [ASSERTED AS &Assertion] + [ABSENT-MATCH &absentMatch] + ID &id +} + +DITContextUse ::= SEQUENCE { + attributeType ATTRIBUTE.&id, + mandatoryContexts [1] SET SIZE (1..MAX) OF CONTEXT.&id OPTIONAL, + optionalContexts [2] SET SIZE (1..MAX) OF CONTEXT.&id OPTIONAL +} + +DIT-CONTEXT-USE-RULE ::= CLASS { + &attributeType ATTRIBUTE.&id UNIQUE, + &Mandatory CONTEXT OPTIONAL, + &Optional CONTEXT OPTIONAL +} +WITH SYNTAX { + ATTRIBUTE TYPE &attributeType + [MANDATORY CONTEXTS &Mandatory] + [OPTIONAL CONTEXTS &Optional] +} + +FRIENDS ::= CLASS { + &anchor ATTRIBUTE.&id UNIQUE, + &Friends ATTRIBUTE +}WITH SYNTAX {ANCHOR &anchor + FRIENDS &Friends +} + +-- system schema information objects +-- object classes +subentry OBJECT-CLASS ::= { + SUBCLASS OF {top} + KIND structural + MUST CONTAIN {commonName | subtreeSpecification} + ID id-sc-subentry +} + +subentryNameForm NAME-FORM ::= { + NAMES subentry + WITH ATTRIBUTES {commonName} + ID id-nf-subentryNameForm +} + +subtreeSpecification ATTRIBUTE ::= { + WITH SYNTAX SubtreeSpecification + USAGE directoryOperation + ID id-oa-subtreeSpecification +} + +administrativeRole ATTRIBUTE ::= { + WITH SYNTAX OBJECT-CLASS.&id + EQUALITY MATCHING RULE objectIdentifierMatch + USAGE directoryOperation + ID id-oa-administrativeRole +} + +createTimestamp ATTRIBUTE ::= { + WITH SYNTAX GeneralizedTime + -- as per 46.3 b) or c) of ITU-T Rec. X.680 | ISO/IEC 8824-1 + EQUALITY MATCHING RULE generalizedTimeMatch + ORDERING MATCHING RULE generalizedTimeOrderingMatch + SINGLE VALUE TRUE + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-createTimestamp +} + +modifyTimestamp ATTRIBUTE ::= { + WITH SYNTAX GeneralizedTime + -- as per 46.3 b) or c) of ITU-T Rec. X.680 | ISO/IEC 8824-1 + EQUALITY MATCHING RULE generalizedTimeMatch + ORDERING MATCHING RULE generalizedTimeOrderingMatch + SINGLE VALUE TRUE + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-modifyTimestamp +} + +subschemaTimestamp ATTRIBUTE ::= { + WITH SYNTAX GeneralizedTime + -- as per 46.3 b) or c) of ITU-T Rec. X.680 | ISO/IEC 8824-1 + EQUALITY MATCHING RULE generalizedTimeMatch + ORDERING MATCHING RULE generalizedTimeOrderingMatch + SINGLE VALUE TRUE + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-subschemaTimestamp +} + +creatorsName ATTRIBUTE ::= { + WITH SYNTAX DistinguishedName + EQUALITY MATCHING RULE distinguishedNameMatch + SINGLE VALUE TRUE + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-creatorsName +} + +modifiersName ATTRIBUTE ::= { + WITH SYNTAX DistinguishedName + EQUALITY MATCHING RULE distinguishedNameMatch + SINGLE VALUE TRUE + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-modifiersName +} + +subschemaSubentryList ATTRIBUTE ::= { + WITH SYNTAX DistinguishedName + EQUALITY MATCHING RULE distinguishedNameMatch + SINGLE VALUE TRUE + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-subschemaSubentryList +} + +accessControlSubentryList ATTRIBUTE ::= { + WITH SYNTAX DistinguishedName + EQUALITY MATCHING RULE distinguishedNameMatch + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-accessControlSubentryList +} + +collectiveAttributeSubentryList ATTRIBUTE ::= { + WITH SYNTAX DistinguishedName + EQUALITY MATCHING RULE distinguishedNameMatch + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-collectiveAttributeSubentryList +} + +contextDefaultSubentryList ATTRIBUTE ::= { + WITH SYNTAX DistinguishedName + EQUALITY MATCHING RULE distinguishedNameMatch + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-contextDefaultSubentryList +} + +serviceAdminSubentryList ATTRIBUTE ::= { + WITH SYNTAX DistinguishedName + EQUALITY MATCHING RULE distinguishedNameMatch + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-serviceAdminSubentryList +} + +hasSubordinates ATTRIBUTE ::= { + WITH SYNTAX BOOLEAN + EQUALITY MATCHING RULE booleanMatch + SINGLE VALUE TRUE + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-hasSubordinates +} + +accessControlSubentry OBJECT-CLASS ::= { + KIND auxiliary + ID id-sc-accessControlSubentry +} + +collectiveAttributeSubentry OBJECT-CLASS ::= { + KIND auxiliary + ID id-sc-collectiveAttributeSubentry +} + +collectiveExclusions ATTRIBUTE ::= { + WITH SYNTAX OBJECT IDENTIFIER + EQUALITY MATCHING RULE objectIdentifierMatch + USAGE directoryOperation + ID id-oa-collectiveExclusions +} + +contextAssertionSubentry OBJECT-CLASS ::= { + KIND auxiliary + MUST CONTAIN {contextAssertionDefaults} + ID id-sc-contextAssertionSubentry +} + +contextAssertionDefaults ATTRIBUTE ::= { + WITH SYNTAX TypeAndContextAssertion + EQUALITY MATCHING RULE objectIdentifierFirstComponentMatch + USAGE directoryOperation + ID id-oa-contextAssertionDefault +} + +serviceAdminSubentry OBJECT-CLASS ::= { + KIND auxiliary + MUST CONTAIN {searchRules} + ID id-sc-serviceAdminSubentry +} + +searchRules ATTRIBUTE ::= { + WITH SYNTAX SearchRuleDescription + EQUALITY MATCHING RULE integerFirstComponentMatch + USAGE directoryOperation + ID id-oa-searchRules +} + +SearchRuleDescription ::= SEQUENCE { + COMPONENTS OF SearchRule, + name [28] SET SIZE (1..MAX) OF UnboundedDirectoryString OPTIONAL, + description [29] UnboundedDirectoryString OPTIONAL +} + +hierarchyLevel ATTRIBUTE ::= { + WITH SYNTAX HierarchyLevel + EQUALITY MATCHING RULE integerMatch + ORDERING MATCHING RULE integerOrderingMatch + SINGLE VALUE TRUE + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-hierarchyLevel +} + +HierarchyLevel ::= INTEGER + +hierarchyBelow ATTRIBUTE ::= { + WITH SYNTAX HierarchyBelow + EQUALITY MATCHING RULE booleanMatch + SINGLE VALUE TRUE + NO USER MODIFICATION TRUE + USAGE directoryOperation + ID id-oa-hierarchyBelow +} + +HierarchyBelow ::= BOOLEAN + +hierarchyParent ATTRIBUTE ::= { + WITH SYNTAX DistinguishedName + EQUALITY MATCHING RULE distinguishedNameMatch + SINGLE VALUE TRUE + USAGE directoryOperation + ID id-oa-hierarchyParent +} + +hierarchyTop ATTRIBUTE ::= { + WITH SYNTAX DistinguishedName + EQUALITY MATCHING RULE distinguishedNameMatch + SINGLE VALUE TRUE + USAGE directoryOperation + ID id-oa-hierarchyTop +} + +-- object identifier assignments +-- object classes +id-oc-top OBJECT IDENTIFIER ::= + {id-oc 0} + +id-oc-alias OBJECT IDENTIFIER ::= {id-oc 1} + +id-oc-parent OBJECT IDENTIFIER ::= {id-oc 28} + +id-oc-child OBJECT IDENTIFIER ::= {id-oc 29} + +-- attributes +id-at-objectClass OBJECT IDENTIFIER ::= {id-at 0} + +id-at-aliasedEntryName OBJECT IDENTIFIER ::= {id-at 1} + +-- matching rules +id-mr-objectIdentifierMatch OBJECT IDENTIFIER ::= {id-mr 0} + +id-mr-distinguishedNameMatch OBJECT IDENTIFIER ::= {id-mr 1} + +-- operational attributes +id-oa-excludeAllCollectiveAttributes OBJECT IDENTIFIER ::= + {id-oa 0} + +id-oa-createTimestamp OBJECT IDENTIFIER ::= {id-oa 1} + +id-oa-modifyTimestamp OBJECT IDENTIFIER ::= {id-oa 2} + +id-oa-creatorsName OBJECT IDENTIFIER ::= {id-oa 3} + +id-oa-modifiersName OBJECT IDENTIFIER ::= {id-oa 4} + +id-oa-administrativeRole OBJECT IDENTIFIER ::= {id-oa 5} + +id-oa-subtreeSpecification OBJECT IDENTIFIER ::= {id-oa 6} + +id-oa-collectiveExclusions OBJECT IDENTIFIER ::= {id-oa 7} + +id-oa-subschemaTimestamp OBJECT IDENTIFIER ::= {id-oa 8} + +id-oa-hasSubordinates OBJECT IDENTIFIER ::= {id-oa 9} + +id-oa-subschemaSubentryList OBJECT IDENTIFIER ::= {id-oa 10} + +id-oa-accessControlSubentryList OBJECT IDENTIFIER ::= {id-oa 11} + +id-oa-collectiveAttributeSubentryList OBJECT IDENTIFIER ::= {id-oa 12} + +id-oa-contextDefaultSubentryList OBJECT IDENTIFIER ::= {id-oa 13} + +id-oa-contextAssertionDefault OBJECT IDENTIFIER ::= {id-oa 14} + +id-oa-serviceAdminSubentryList OBJECT IDENTIFIER ::= {id-oa 15} + +id-oa-searchRules OBJECT IDENTIFIER ::= {id-oa 16} + +id-oa-hierarchyLevel OBJECT IDENTIFIER ::= {id-oa 17} + +id-oa-hierarchyBelow OBJECT IDENTIFIER ::= {id-oa 18} + +id-oa-hierarchyParent OBJECT IDENTIFIER ::= {id-oa 19} + +id-oa-hierarchyTop OBJECT IDENTIFIER ::= {id-oa 20} + +-- subentry classes +id-sc-subentry OBJECT IDENTIFIER ::= {id-sc 0} + +id-sc-accessControlSubentry OBJECT IDENTIFIER ::= {id-sc 1} + +id-sc-collectiveAttributeSubentry OBJECT IDENTIFIER ::= {id-sc 2} + +id-sc-contextAssertionSubentry OBJECT IDENTIFIER ::= {id-sc 3} + +id-sc-serviceAdminSubentry OBJECT IDENTIFIER ::= {id-sc 4} + +-- Name forms +id-nf-subentryNameForm OBJECT IDENTIFIER ::= {id-nf 16} + +-- administrative roles +id-ar-autonomousArea OBJECT IDENTIFIER ::= {id-ar 1} + +id-ar-accessControlSpecificArea OBJECT IDENTIFIER ::= {id-ar 2} + +id-ar-accessControlInnerArea OBJECT IDENTIFIER ::= {id-ar 3} + +id-ar-subschemaAdminSpecificArea OBJECT IDENTIFIER ::= {id-ar 4} + +id-ar-collectiveAttributeSpecificArea OBJECT IDENTIFIER ::= {id-ar 5} + +id-ar-collectiveAttributeInnerArea OBJECT IDENTIFIER ::= {id-ar 6} + +id-ar-contextDefaultSpecificArea OBJECT IDENTIFIER ::= {id-ar 7} + +id-ar-serviceSpecificArea OBJECT IDENTIFIER ::= {id-ar 8} + +END -- InformationFramework diff --git a/lib/public_key/asn1/Makefile b/lib/public_key/asn1/Makefile index c4f8d65aa7..e56e7e73ad 100644 --- a/lib/public_key/asn1/Makefile +++ b/lib/public_key/asn1/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 @@ -38,12 +38,12 @@ RELSYSDIR = $(RELEASE_PATH)/lib/public_key-$(VSN) .SUFFIXES: .asn1 .PRECIOUS: %.erl -ASN_TOP = OTP-PUB-KEY +ASN_TOP = OTP-PUB-KEY PKCS-FRAME ASN_MODULES = PKIX1Explicit88 PKIX1Implicit88 PKIX1Algorithms88 \ - PKIXAttributeCertificate PKCS-1 PKCS-3 OTP-PKIX + PKIXAttributeCertificate PKCS-1 PKCS-3 PKCS-8 InformationFramework PKCS5v2-0 OTP-PKIX ASN_ASNS = $(ASN_MODULES:%=%.asn1) -ASN_ERLS = $(ASN_TOP).erl -ASN_HRLS = $(ASN_TOP).hrl +ASN_ERLS = $(ASN_TOP:%=%.erl) +ASN_HRLS = $(ASN_TOP:%=%.hrl) ASN_CONFIGS = OTP-PUB-KEY.asn1config ASN_DBS = $(ASN_MODULES:%=%.asn1db) OTP-PUB-KEY.asn1db ASN_TABLES = $(ASN_MODULES:%=%.table) @@ -83,7 +83,7 @@ docs: erlc $(ASN_FLAGS) $< $(HRL_FILES): $(ASN_HRLS) - cp -p $(ASN_HRLS) $(INCLUDE) + cp -p $^ $(INCLUDE) # ---------------------------------------------------- # Release Target @@ -113,3 +113,9 @@ OTP-PUB-KEY.asn1db: PKIX1Algorithms88.asn1 \ PKCS-1.asn1\ PKCS-3.asn1\ OTP-PKIX.asn1 + +$(EBIN)/PKCS-FRAME.beam: PKCS-FRAME.erl PKCS-FRAME.hrl +PKCS-FRAME.erl PKCS-FRAME.hrl: PKCS-FRAME.asn1db +PKCS-FRAME.asn1db: PKCS-8.asn1\ + InformationFramework.asn1\ + PKCS5v2-0.asn1 \ No newline at end of file diff --git a/lib/public_key/asn1/PKCS-8.asn1 b/lib/public_key/asn1/PKCS-8.asn1 new file mode 100644 index 0000000000..7413519b57 --- /dev/null +++ b/lib/public_key/asn1/PKCS-8.asn1 @@ -0,0 +1,83 @@ +PKCS-8 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-8(8) + modules(1) pkcs-8(1)} + +-- $Revision: 1.5 $ + +-- This module has been checked for conformance with the ASN.1 +-- standard by the OSS ASN.1 Tools + +DEFINITIONS IMPLICIT TAGS ::= + +BEGIN + +-- EXPORTS All -- +-- All types and values defined in this module is exported for use in other +-- ASN.1 modules. + +IMPORTS + +-- informationFramework +-- FROM UsefulDefinitions {joint-iso-itu-t(2) ds(5) module(1) +-- usefulDefinitions(0) 3} + +Attribute +-- FROM InformationFramework informationFramework + FROM InformationFramework; + +-- This import is really unnecessary since ALGORITHM-IDENTIFIER is defined as a +-- TYPE-IDENTIFIER +-- Renome this import and replace all occurences of ALGORITHM-IDENTIFIER with +-- TYPE-IDENTIFIER as a workaround for weaknesses in the ASN.1 compiler +--AlgorithmIdentifier, ALGORITHM-IDENTIFIER +-- FROM PKCS5v2-0 {iso(1) member-body(2) us(840) rsadsi(113549) +-- pkcs(1) pkcs-5(5) modules(16) pkcs-5(1)}; + +-- Inlined from PKCS5v2-0 since it is the only thing imported from that module +-- AlgorithmIdentifier { ALGORITHM-IDENTIFIER:InfoObjectSet } ::= +AlgorithmIdentifier { TYPE-IDENTIFIER:InfoObjectSet } ::= +SEQUENCE { +-- algorithm ALGORITHM-IDENTIFIER.&id({InfoObjectSet}), + algorithm TYPE-IDENTIFIER.&id({InfoObjectSet}), +-- parameters ALGORITHM-IDENTIFIER.&Type({InfoObjectSet} + parameters TYPE-IDENTIFIER.&Type({InfoObjectSet} + {@algorithm}) OPTIONAL } + +-- Private-key information syntax + +PrivateKeyInfo ::= SEQUENCE { + version Version, +-- privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}}, + privateKeyAlgorithm AlgorithmIdentifier {{...}}, + privateKey PrivateKey, + attributes [0] Attributes OPTIONAL } + +Version ::= INTEGER {v1(0)} (v1,...) + +PrivateKey ::= OCTET STRING + +-- Attributes ::= SET OF Attribute +Attributes ::= SET OF Attribute {{...}} + +-- Encrypted private-key information syntax + +EncryptedPrivateKeyInfo ::= SEQUENCE { +-- encryptionAlgorithm AlgorithmIdentifier {{KeyEncryptionAlgorithms}}, + encryptionAlgorithm AlgorithmIdentifier {{...}}, + encryptedData EncryptedData +} + +EncryptedData ::= OCTET STRING + +-- PrivateKeyAlgorithms ALGORITHM-IDENTIFIER ::= { +PrivateKeyAlgorithms TYPE-IDENTIFIER ::= { + ... -- For local profiles +} + +-- KeyEncryptionAlgorithms ALGORITHM-IDENTIFIER ::= { +KeyEncryptionAlgorithms TYPE-IDENTIFIER ::= { + ... -- For local profiles +} + +END + + diff --git a/lib/public_key/asn1/PKCS-FRAME.set.asn b/lib/public_key/asn1/PKCS-FRAME.set.asn new file mode 100644 index 0000000000..a0777ff260 --- /dev/null +++ b/lib/public_key/asn1/PKCS-FRAME.set.asn @@ -0,0 +1,3 @@ +PKCS-8.asn1 +InformationFramework.asn1 +PKCS5v2-0.asn1 diff --git a/lib/public_key/asn1/PKCS5v2-0.asn1 b/lib/public_key/asn1/PKCS5v2-0.asn1 new file mode 100644 index 0000000000..fe7e16c7fa --- /dev/null +++ b/lib/public_key/asn1/PKCS5v2-0.asn1 @@ -0,0 +1,142 @@ +-- PKCS #5 v2.0 ASN.1 Module +-- Revised March 25, 1999 + +-- This module has been checked for conformance with the +-- ASN.1 standard by the OSS ASN.1 Tools + +PKCS5v2-0 {iso(1) member-body(2) us(840) rsadsi(113549) + pkcs(1) pkcs-5(5) modules(16) pkcs5v2-0(1)} + +DEFINITIONS ::= BEGIN + +-- Basic object identifiers + +rsadsi OBJECT IDENTIFIER ::= + {iso(1) member-body(2) us(840) 113549} +pkcs OBJECT IDENTIFIER ::= {rsadsi 1} +pkcs-5 OBJECT IDENTIFIER ::= {pkcs 5} + +-- Basic types and classes + +AlgorithmIdentifier { TYPE-IDENTIFIER:InfoObjectSet } ::= +SEQUENCE { + algorithm TYPE-IDENTIFIER.&id({InfoObjectSet}), + parameters TYPE-IDENTIFIER.&Type({InfoObjectSet} + {@algorithm}) OPTIONAL } + +--ALGORITHM-IDENTIFIER ::= TYPE-IDENTIFIER + +-- PBKDF2 + +-- PBKDF2Algorithms ALGORITHM-IDENTIFIER ::= +-- { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ...} + +id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12} + +-- algid-hmacWithSHA1 AlgorithmIdentifier {{PBKDF2-PRFs}} ::= +-- {algorithm id-hmacWithSHA1, parameters NULL : NULL} + +PBKDF2-params ::= SEQUENCE { + salt CHOICE { + specified OCTET STRING, + otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}} + }, + iterationCount INTEGER (1..MAX), + keyLength INTEGER (1..MAX) OPTIONAL, + prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT +{algorithm id-hmacWithSHA1, parameters NULL : NULL}} +-- algid-hmacWithSHA1 } + +PBKDF2-SaltSources TYPE-IDENTIFIER ::= { ... } + +PBKDF2-PRFs TYPE-IDENTIFIER ::= + { {NULL IDENTIFIED BY id-hmacWithSHA1}, ... } + + -- PBES1 + +PBES1Algorithms TYPE-IDENTIFIER ::= + { {PBEParameter IDENTIFIED BY pbeWithMD2AndDES-CBC} | + {PBEParameter IDENTIFIED BY pbeWithMD2AndRC2-CBC} | + {PBEParameter IDENTIFIED BY pbeWithMD5AndDES-CBC} | + {PBEParameter IDENTIFIED BY pbeWithMD5AndRC2-CBC} | + {PBEParameter IDENTIFIED BY pbeWithSHA1AndDES-CBC} | + {PBEParameter IDENTIFIED BY pbeWithSHA1AndRC2-CBC}, ...} + +pbeWithMD2AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 1} +pbeWithMD2AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 4} +pbeWithMD5AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 3} +pbeWithMD5AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 6} +pbeWithSHA1AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 10} +pbeWithSHA1AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 11} + +PBEParameter ::= SEQUENCE { + salt OCTET STRING (SIZE(8)), + iterationCount INTEGER } + +-- PBES2 + +PBES2Algorithms TYPE-IDENTIFIER ::= + { {PBES2-params IDENTIFIED BY id-PBES2}, ...} + +id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13} + +PBES2-params ::= SEQUENCE { + keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, + encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} } + +PBES2-KDFs TYPE-IDENTIFIER ::= + { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... } + +PBES2-Encs TYPE-IDENTIFIER ::= { ... } + +-- PBMAC1 + +PBMAC1Algorithms TYPE-IDENTIFIER ::= + { {PBMAC1-params IDENTIFIED BY id-PBMAC1}, ...} + +id-PBMAC1 OBJECT IDENTIFIER ::= {pkcs-5 14} + +PBMAC1-params ::= SEQUENCE { + keyDerivationFunc AlgorithmIdentifier {{PBMAC1-KDFs}}, + messageAuthScheme AlgorithmIdentifier {{PBMAC1-MACs}} } + +PBMAC1-KDFs TYPE-IDENTIFIER ::= + { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... } + +PBMAC1-MACs TYPE-IDENTIFIER ::= { ... } + +-- Supporting techniques + +digestAlgorithm OBJECT IDENTIFIER ::= {rsadsi 2} +encryptionAlgorithm OBJECT IDENTIFIER ::= {rsadsi 3} + +SupportingAlgorithms TYPE-IDENTIFIER ::= + { {NULL IDENTIFIED BY id-hmacWithSHA1} | + {OCTET STRING (SIZE(8)) IDENTIFIED BY desCBC} | + {OCTET STRING (SIZE(8)) IDENTIFIED BY des-EDE3-CBC} | + {RC2-CBC-Parameter IDENTIFIED BY rc2CBC} | + {RC5-CBC-Parameters IDENTIFIED BY rc5-CBC-PAD}, ... } + +id-hmacWithSHA1 OBJECT IDENTIFIER ::= {digestAlgorithm 7} + +desCBC OBJECT IDENTIFIER ::= + {iso(1) identified-organization(3) oiw(14) secsig(3) + algorithms(2) 7} -- from OIW + +des-EDE3-CBC OBJECT IDENTIFIER ::= {encryptionAlgorithm 7} + +rc2CBC OBJECT IDENTIFIER ::= {encryptionAlgorithm 2} + +RC2-CBC-Parameter ::= SEQUENCE { + rc2ParameterVersion INTEGER OPTIONAL, + iv OCTET STRING (SIZE(8)) } + +rc5-CBC-PAD OBJECT IDENTIFIER ::= {encryptionAlgorithm 9} + +RC5-CBC-Parameters ::= SEQUENCE { + version INTEGER {v1-0(16)}, -- (v1-0), + rounds INTEGER (8..127), + blockSizeInBits INTEGER (64 | 128), + iv OCTET STRING OPTIONAL } + +END diff --git a/lib/public_key/include/public_key.hrl b/lib/public_key/include/public_key.hrl index 5f97d80f7e..2475295974 100644 --- a/lib/public_key/include/public_key.hrl +++ b/lib/public_key/include/public_key.hrl @@ -23,6 +23,7 @@ -define(public_key, true). -include("OTP-PUB-KEY.hrl"). +-include("PKCS-FRAME.hrl"). -record('SubjectPublicKeyInfoAlgorithm', { algorithm, diff --git a/lib/public_key/src/Makefile b/lib/public_key/src/Makefile index 5a24b02d2a..062c495a65 100644 --- a/lib/public_key/src/Makefile +++ b/lib/public_key/src/Makefile @@ -42,10 +42,11 @@ MODULES = \ public_key \ pubkey_pem \ pubkey_ssh \ + pubkey_pbe \ pubkey_cert \ - pubkey_cert_records + pubkey_cert_records -HRL_FILES = $(INCLUDE)/public_key.hrl +HRL_FILES = $(INCLUDE)/public_key.hrl INTERNAL_HRL_FILES = @@ -109,4 +110,3 @@ release_spec: opt $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin release_docs_spec: - diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl new file mode 100644 index 0000000000..f471871d35 --- /dev/null +++ b/lib/public_key/src/pubkey_pbe.erl @@ -0,0 +1,181 @@ +%% +%% %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% +%% +%% Description: Implements Password Based Encryption PKCS-5, RFC-2898 + +-module(pubkey_pbe). + +-include("public_key.hrl"). + +-export([encode/4, decode/4, decrypt_parameters/1]). +-export([pbdkdf1/4, pbdkdf2/6]). + +-define(DEFAULT_SHA_MAC_KEYLEN, 20). + +-define(OCTET_STR, 4). +-define(IV_LEN, 8). + +%%==================================================================== +%% Internal application API +%%==================================================================== + +pbdkdf2(Password, Salt, Count, DerivedKeyLen, PrfLen, Prf)-> + NumBlocks = ceiling(DerivedKeyLen / PrfLen), + NumLastBlockOctets = DerivedKeyLen - (NumBlocks - 1) * PrfLen , + blocks(NumBlocks, NumLastBlockOctets, 1, Password, Salt, Count, Prf, PrfLen, <<>>). + +encode(Data, Password, "DES-CBC" = Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, derived_key_length(Cipher), KeyDevParams), + crypto:des_cbc_encrypt(Key, IV, Data); + +encode(Data, Password, "DES-EDE3-CBC" = Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, derived_key_length(Cipher), KeyDevParams), + <> = Key, + crypto:des_ede3_cbc_encrypt(Key1, Key2, Key3, IV, Data). + +decode(Data, Password,"DES-CBC"= Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, derived_key_length(Cipher), KeyDevParams), + crypto:des_cbc_decrypt(Key, IV, Data); + +decode(Data, Password,"DES-EDE3-CBC" = Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, derived_key_length(Cipher), KeyDevParams), + <> = Key, + crypto:des_ede3_cbc_decrypt(Key1, Key2, Key3, IV, Data). + +%%-------------------------------------------------------------------- +-spec pbdkdf1(string(), iodata(), integer(), atom()) -> binary(). +%% +%% Description: Implements password based decryption key derive function 1. +%% Exported mainly for testing purposes. +%%-------------------------------------------------------------------- +pbdkdf1(_, _, 0, Acc) -> + Acc; +pbdkdf1(Password, Salt, Count, Hash) -> + Result = crypto:Hash([Password, Salt]), + do_pbdkdf1(Result, Count-1, Result, Hash). + +%%-------------------------------------------------------------------- +-spec pbdkdf2(string(), iodata(), integer(), integer(), fun(), integer()) + -> binary(). +%% +%% Description: Implements password based decryption key derive function 2. +%% Exported mainly for testing purposes. +%%-------------------------------------------------------------------- +pbdkdf2(Password, Salt, Count, DerivedKeyLen, Prf, PrfOutputLen)-> + NumBlocks = ceiling(DerivedKeyLen / PrfOutputLen), + NumLastBlockOctets = DerivedKeyLen - (NumBlocks - 1) * PrfOutputLen , + blocks(NumBlocks, NumLastBlockOctets, 1, Password, Salt, + Count, Prf, PrfOutputLen, <<>>). +%%-------------------------------------------------------------------- +-spec decrypt_parameters(#'EncryptedPrivateKeyInfo_encryptionAlgorithm'{}) -> + {Cipher::string(), #'PBES2-params'{}}. +%% +%% Description: Performs ANS1-decoding of encryption parameters. +%%-------------------------------------------------------------------- +decrypt_parameters(#'EncryptedPrivateKeyInfo_encryptionAlgorithm'{ + algorithm = Oid, parameters = Param}) -> + decrypt_parameters(Oid, Param). + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +password_to_key_and_iv(Password, KeyLen, {salt, Salt}) -> + <> = + pem_encrypt(<<>>, Password, Salt, ceiling(KeyLen div 16), <<>>, md5), + %% Old PEM encryption does not use standard encryption method + %% pbdkdf1 and uses then salt as IV + {Key, Salt}. + +pem_encrypt(_, _, _, 0, Acc, _) -> + Acc; +pem_encrypt(Prev, Password, Salt, Count, Acc, Hash) -> + Result = crypto:Hash([Prev, Password, Salt]), + pem_encrypt(Result, Password, Salt, Count-1 , <>, Hash). + +do_pbdkdf1(_, 0, Acc, _) -> + Acc; +do_pbdkdf1(Prev, Count, Acc, Hash) -> + Result = crypto:Hash(Prev), + do_pbdkdf1(Result, Count-1 , <>, Hash). + +iv(#'PBES2-params_encryptionScheme'{algorithm = Algo, + parameters = ASNIV}) when (Algo == ?'desCBC') or + (Algo == ?'des-EDE3-CBC') -> + %% This is an so called open ASN1-type that in this + %% case will be an octet-string of length 8 + <> = ASNIV, + IV. + +blocks(1, N, Index, Password, Salt, Count, Prf, PrfLen, Acc) -> + <> = xor_sum(Password, Salt, Count, Index, Prf, PrfLen), + <>; +blocks(NumBlocks, N, Index, Password, Salt, Count, Prf, PrfLen, Acc) -> + XorSum = xor_sum(Password, Salt, Count, Index, Prf, PrfLen), + blocks(NumBlocks -1, N, Index +1, Password, Salt, Count, Prf, PrfLen, <>). + +xor_sum(Password, Salt, Count, Index, Prf, PrfLen) -> + %%Result = Prf(Password, [Salt,<>], PrfLen), + Result = Prf(Password, [Salt,<>]), + do_xor_sum(Prf, PrfLen, Result, Password, Count-1, Result). + +do_xor_sum(_, _, _, _, 0, Acc) -> + Acc; +do_xor_sum(Prf, PrfLen, Prev, Password, Count, Acc)-> + %%Result = Prf(Password, Prev, PrfLen), + Result = Prf(Password, Prev), + do_xor_sum(Prf, PrfLen, Result, Password, Count-1, crypto:exor(Acc, Result)). + +decrypt_parameters(?'id-PBES2', DekParams) -> + {ok, Params} = 'PKCS-FRAME':decode('PBES2-params', DekParams), + {cipher(Params#'PBES2-params'.encryptionScheme), Params}. + +key_derivation_params(#'PBES2-params_keyDerivationFunc'{algorithm = ?'id-PBKDF2', + parameters = + #'PBKDF2-params'{salt = {specified, OctetSalt}, + iterationCount = Count, + keyLength = Length, + prf = Prf}}) -> + PseudoRandomFunction = pseudo_random_function(Prf), + KeyLen = pseudo_key_length(Length, Prf), + {iolist_to_binary(OctetSalt), Count, KeyLen, PseudoRandomFunction}. + +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = {_,_, _,'id-hmacWithSHA1'}}) -> + %%fun crypto:sha_mac_n/3. + fun crypto:sha_mac/2. + +pseudo_key_length(asn1_NOVALUE, #'PBKDF2-params_prf'{algorithm = {_,_, _,'id-hmacWithSHA1'}}) -> + ?DEFAULT_SHA_MAC_KEYLEN; +pseudo_key_length(Len, _) when is_integer(Len) -> + Len. + +derived_key_length("DES-CBC") -> + 8; +%% derived_key_length("RC2-CBC") -> +%% 5; +derived_key_length("DES-EDE3-CBC") -> + 24. + +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'desCBC'}) -> + "DES-CBC"; +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'des-EDE3-CBC'}) -> + "DES-EDE3-CBC". +%% cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'rc2CBC'}) -> +%% "RC2-CBC". + +ceiling(Float) -> + erlang:round(Float + 0.5). diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl index c26815bc04..f19aab0533 100644 --- a/lib/public_key/src/pubkey_pem.erl +++ b/lib/public_key/src/pubkey_pem.erl @@ -44,7 +44,7 @@ -export([encode/1, decode/1, decipher/2, cipher/3]). %% Backwards compatibility --export([decode_key/2]). +%%-export([decode_key/2]). -define(ENCODED_LINE_LENGTH, 64). @@ -69,23 +69,27 @@ encode(PemEntries) -> encode_pem_entries(PemEntries). %%-------------------------------------------------------------------- --spec decipher({pki_asn1_type(), DerEncrypted::binary(),{Cipher :: string(), - Salt :: binary()}}, - string()) -> Der::binary(). +-spec decipher({pki_asn1_type(), DerEncrypted::binary(), term()}, + %%{Cipher :: string(), + %%Salt :: binary()}}, + string()) -> Der::binary(). %% %% Description: Deciphers a decrypted pem entry. %%-------------------------------------------------------------------- -decipher({_, DecryptDer, {Cipher,Salt}}, Password) -> - decode_key(DecryptDer, Password, Cipher, Salt). +decipher({_, DecryptDer, {Cipher, KeyDevParams}}, Password) -> + %%decode_key(DecryptDer, Password, Cipher, Salt). + pubkey_pbe:decode(DecryptDer, Password, Cipher, KeyDevParams). %%-------------------------------------------------------------------- --spec cipher(Der::binary(),{Cipher :: string(), Salt :: binary()} , +-spec cipher(Der::binary(), term(), +%%{Cipher :: string(), Hash::atom(), Salt :: binary()} , string()) -> binary(). %% %% Description: Ciphers a PEM entry %%-------------------------------------------------------------------- -cipher(Der, {Cipher,Salt}, Password)-> - encode_key(Der, Password, Cipher, Salt). +cipher(Der, {Cipher, KeyDevParams}, Password)-> + %%encode_key(Der, Password, Cipher, Salt). + pubkey_pbe:encode(Der, Password, Cipher, KeyDevParams). %%-------------------------------------------------------------------- %%% Internal functions @@ -96,7 +100,7 @@ encode_pem_entries(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}}) -> +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"]. @@ -122,13 +126,25 @@ decode_pem_entry(Start, [<<"Proc-Type: 4,ENCRYPTED", _/binary>>, Line | Lines]) Decoded = base64:mime_decode(Cs), [_, DekInfo0] = string:tokens(binary_to_list(Line), ": "), [Cipher, Salt] = string:tokens(DekInfo0, ","), - {Type, Decoded, {Cipher, unhex(Salt)}}; + {Type, Decoded, {Cipher, {salt, 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}. + case Type of + 'EncryptedPrivateKeyInfo'-> + decode_encrypted_private_keyinfo(Decoded); + _ -> + {Type, Decoded, not_encrypted} + end. + +decode_encrypted_private_keyinfo(Der) -> + #'EncryptedPrivateKeyInfo'{encryptionAlgorithm = AlgorithmInfo, + encryptedData = Data} = public_key:der_decode('EncryptedPrivateKeyInfo', Der), + DecryptParams = pubkey_pbe:decrypt_parameters(AlgorithmInfo), + {'PrivateKeyInfo', iolist_to_binary(Data), DecryptParams}. + split_bin(Bin) -> split_bin(0, Bin). @@ -160,36 +176,36 @@ join_entry([<<"-----END ", _/binary>>| Lines], Entry) -> join_entry([Line | Lines], Entry) -> join_entry(Lines, [Line | Entry]). -decode_key(Data, Password, "DES-CBC", Salt) -> - Key = password_to_key(Password, Salt, 8), - IV = Salt, - crypto:des_cbc_decrypt(Key, IV, Data); -decode_key(Data, Password, "DES-EDE3-CBC", Salt) -> - Key = password_to_key(Password, Salt, 24), - IV = Salt, - <> = 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, - <> = Key, - crypto:des_ede3_cbc_encrypt(Key1, Key2, Key3, IV, Data). - -password_to_key(Data, Salt, KeyLen) -> - <> = - password_to_key(<<>>, Data, Salt, KeyLen, <<>>), - Key. - -password_to_key(_, _, _, Len, Acc) when Len =< 0 -> - Acc; -password_to_key(Prev, Data, Salt, Len, Acc) -> - M = crypto:md5([Prev, Data, Salt]), - password_to_key(M, Data, Salt, Len - size(M), <>). +%% decode_key(Data, Password, "DES-CBC", Salt) -> +%% Key = password_to_key(Password, Salt, 8), +%% IV = Salt, +%% crypto:des_cbc_decrypt(Key, IV, Data); +%% decode_key(Data, Password, "DES-EDE3-CBC", Salt) -> +%% Key = password_to_key(Password, Salt, 24), +%% IV = Salt, +%% <> = 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, +%% <> = Key, +%% crypto:des_ede3_cbc_encrypt(Key1, Key2, Key3, IV, Data). + +%% password_to_key(Data, Salt, KeyLen) -> +%% <> = +%% password_to_key(<<>>, Data, Salt, KeyLen, <<>>), +%% Key. + +%% password_to_key(_, _, _, Len, Acc) when Len =< 0 -> +%% Acc; +%% password_to_key(Prev, Data, Salt, Len, Acc) -> +%% M = crypto:md5([Prev, Data, Salt]), +%% password_to_key(M, Data, Salt, Len - size(M), <>). unhex(S) -> unhex(S, []). @@ -228,6 +244,10 @@ pem_end(<<"-----BEGIN DSA PRIVATE KEY-----">>) -> <<"-----END DSA PRIVATE KEY-----">>; pem_end(<<"-----BEGIN DH PARAMETERS-----">>) -> <<"-----END DH PARAMETERS-----">>; +pem_end(<<"-----BEGIN PRIVATE KEY-----">>) -> + <<"-----END PRIVATE KEY-----">>; +pem_end(<<"-----BEGIN ENCRYPTED PRIVATE KEY-----">>) -> + <<"-----END ENCRYPTED PRIVATE KEY-----">>; pem_end(_) -> undefined. @@ -242,7 +262,11 @@ asn1_type(<<"-----BEGIN PUBLIC KEY-----">>) -> asn1_type(<<"-----BEGIN DSA PRIVATE KEY-----">>) -> 'DSAPrivateKey'; asn1_type(<<"-----BEGIN DH PARAMETERS-----">>) -> - 'DHParameter'. + 'DHParameter'; +asn1_type(<<"-----BEGIN PRIVATE KEY-----">>) -> + 'PrivateKeyInfo'; +asn1_type(<<"-----BEGIN ENCRYPTED PRIVATE KEY-----">>) -> + 'EncryptedPrivateKeyInfo'. pem_decrypt() -> <<"Proc-Type: 4,ENCRYPTED">>. @@ -253,7 +277,7 @@ pem_decrypt_info(Cipher, Salt) -> %%-------------------------------------------------------------------- %%% Deprecated %%-------------------------------------------------------------------- -decode_key({_Type, Bin, not_encrypted}, _) -> - Bin; -decode_key({_Type, Bin, {Chipher,Salt}}, Password) -> - decode_key(Bin, Password, Chipher, Salt). +%% 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/public_key.app.src b/lib/public_key/src/public_key.app.src index 1963bd05d4..4cc81ea573 100644 --- a/lib/public_key/src/public_key.app.src +++ b/lib/public_key/src/public_key.app.src @@ -3,10 +3,12 @@ {vsn, "%VSN%"}, {modules, [ public_key, pubkey_pem, + pubkey_pbe, pubkey_ssh, pubkey_cert, pubkey_cert_records, - 'OTP-PUB-KEY' + 'OTP-PUB-KEY', + 'PKCS-FRAME' ]}, {applications, [crypto, kernel, stdlib]}, {registered, []}, diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 33fcce2c44..68c7b7ad93 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -46,11 +46,11 @@ ]). %% Deprecated --export([decode_private_key/1, decode_private_key/2, pem_to_der/1]). +%% -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}). +%% -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'. @@ -104,22 +104,20 @@ pem_entry_decode({Asn1Type, Der, not_encrypted}) when is_atom(Asn1Type), 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, +pem_entry_decode({Asn1Type, CryptDer, {Cipher, _Params}} = PemEntry, Password) when is_atom(Asn1Type), is_binary(CryptDer), - is_list(Cipher), - is_binary(Salt), - erlang:byte_size(Salt) == 8 - -> + is_list(Cipher) -> 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()}) -> + %%{{Cipher :: string(), Salt :: binary()}, string()} + term()) -> pem_entry(). -% + % %% Description: Creates a pem entry that can be feed to pem_encode/1. %%-------------------------------------------------------------------- pem_entry_encode('SubjectPublicKeyInfo', Entity=#'RSAPublicKey'{}) -> @@ -137,11 +135,11 @@ pem_entry_encode('SubjectPublicKeyInfo', 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, +pem_entry_encode(Asn1Type, Entity, {CipherInfo, Password}) when is_atom(Asn1Type), + %%is_list(Cipher), + %%is_binary(Salt), + %%is_atom(Hash), + %% erlang:byte_size(Salt) == 8, is_list(Password)-> Der = der_encode(Asn1Type, Entity), DecryptDer = pubkey_pem:cipher(Der, CipherInfo, Password), @@ -152,6 +150,17 @@ pem_entry_encode(Asn1Type, Entity, %% %% Description: Decodes a public key asn1 der encoded entity. %%-------------------------------------------------------------------- +der_decode(Asn1Type, Der) when (Asn1Type == 'PrivateKeyInfo') or (Asn1Type == 'EncryptedPrivateKeyInfo') + andalso is_binary(Der) -> + try + {ok, Decoded} = 'PKCS-FRAME':decode(Asn1Type, Der), + + Decoded + catch + error:{badmatch, {error, _}} = Error -> + erlang:error(Error) + end; + der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) -> try {ok, Decoded} = 'OTP-PUB-KEY':decode(Asn1Type, Der), @@ -166,6 +175,15 @@ der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) -> %% %% Description: Encodes a public key entity with asn1 DER encoding. %%-------------------------------------------------------------------- +der_encode(Asn1Type, Entity) when Asn1Type == 'PrivateKeyInfo'; Asn1Type == 'EncryptedPrivateKeyInfo' -> + try + {ok, Encoded} = 'PKCS-FRAME':encode(Asn1Type, Entity), + iolist_to_binary(Encoded) + catch + error:{badmatch, {error, _}} = Error -> + erlang:error(Error) + end; + der_encode(Asn1Type, Entity) when is_atom(Asn1Type) -> try {ok, Encoded} = 'OTP-PUB-KEY':encode(Asn1Type, Entity), @@ -636,16 +654,16 @@ sized_binary(Binary) -> %%-------------------------------------------------------------------- %%% Deprecated functions %%-------------------------------------------------------------------- -pem_to_der(CertSource) -> - {ok, Bin} = file:read_file(CertSource), - {ok, pubkey_pem:decode(Bin)}. +%% 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) -> +%% 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). +%% 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 6889ae9a8a..b7f91981a5 100644 --- a/lib/public_key/test/Makefile +++ b/lib/public_key/test/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 @@ -30,6 +30,7 @@ INCLUDES= -I. -I ../include MODULES= \ erl_make_certs \ public_key_SUITE \ + pbe_SUITE \ pkits_SUITE ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/public_key/test/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl new file mode 100644 index 0000000000..1d33976505 --- /dev/null +++ b/lib/public_key/test/pbe_SUITE.erl @@ -0,0 +1,251 @@ +%% +%% %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(pbe_SUITE). + +-include_lib("test_server/include/test_server.hrl"). + +%% Note: This directive should only be used in test suites. +-compile(export_all). +%% Test server callback functions +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initialization before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + try crypto:start() of + ok -> + Config + catch _:_ -> + {skip, "Crypto did not start"} + end. +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + application:stop(crypto). + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initialization before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%% Description: Initialization before each test case +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% Function: all(Clause) -> TestCases +%% Clause - atom() - suite | doc +%% TestCases - [Case] +%% Case - atom() +%% Name of a test case. +%% Description: Returns a list of all test cases in this test suite +%%-------------------------------------------------------------------- +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + pbdkdf1, + pbdkdf2, + encrypted_private_key_info]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%% Test cases starts here. +%%-------------------------------------------------------------------- +pbdkdf1(doc) -> + ["Test with PKCS #5 PBKDF1 Test Vectors"]; +pbdkdf1(Config) when is_list(Config) -> + %%Password = "password" + %% = (0x)70617373776F7264 + %%Salt = (0x)78578E5A5D63CB06 + %%Count = 1000 + %%kLen = 16 + %%Key = PBKDF1(Password, Salt, Count, kLen) + %%= (0x)DC19847E05C64D2FAF10EBFB4A3D2A20 + + Password = "password", + Salt = <<16#78,16#57,16#8E,16#5A,16#5D,16#63,16#CB,16#06>>, + Count = 1000, + + <<16#DC, 16#19, 16#84, 16#7E, + 16#05, 16#C6, 16#4D, 16#2F, + 16#AF, 16#10, 16#EB, 16#FB, + 16#4A, 16#3D, 16#2A, 16#20, _/binary>> = + pubkey_pbe:pbdkdf1(Password, Salt, Count, sha). + +pbdkdf2(doc) -> + ["Test with PKCS #5 PBKDF2 Test Vectors"]; +pbdkdf2(Config) when is_list(Config) -> + %% Input: + %% P = "password" (8 octets) + %% S = "salt" (4 octets) + %% c = 1 + %% dkLen = 20 + + %% Output: + %% DK = 0c 60 c8 0f 96 1f 0e 71 + %% f3 a9 b5 24 af 60 12 06 + %% 2f e0 37 a6 (20 octets) + + <<16#0c, 16#60, 16#c8, 16#0f, 16#96, 16#1f, 16#0e, 16#71, + 16#f3, 16#a9, 16#b5, 16#24, 16#af, 16#60, 16#12, 16#06, + 16#2f, 16#e0, 16#37, 16#a6>> = pubkey_pbe:pbdkdf2("password", "salt", 1, 20, fun crypto:sha_mac/3, 20), + + %% Input: + %% P = "password" (8 octets) + %% S = "salt" (4 octets) + %% c = 2 + %% dkLen = 20 + + %% Output: + %% DK = ea 6c 01 4d c7 2d 6f 8c + %% cd 1e d9 2a ce 1d 41 f0 + %% d8 de 89 57 (20 octets) + + <<16#ea, 16#6c, 16#01, 16#4d, 16#c7, 16#2d, 16#6f, 16#8c, + 16#cd, 16#1e, 16#d9, 16#2a, 16#ce, 16#1d, 16#41, 16#f0, + 16#d8, 16#de, 16#89, 16#57>> = + pubkey_pbe:pbdkdf2("password", "salt", 2, 20, 20, fun crypto:sha_mac/2), + + %% Input: + %% P = "password" (8 octets) + %% S = "salt" (4 octets) + %% c = 4096 + %% dkLen = 20 + + %% Output: + %% DK = 4b 00 79 01 b7 65 48 9a + %% be ad 49 d9 26 f7 21 d0 + %% 65 a4 29 c1 (20 octets) + + <<16#4b, 16#00, 16#79, 16#01, 16#b7, 16#65, 16#48, 16#9a, + 16#be, 16#ad, 16#49, 16#d9, 16#26, 16#f7, 16#21, 16#d0, + 16#65, 16#a4, 16#29, 16#c1>> = pubkey_pbe:pbdkdf2("password", "salt", 4096, 20, 20, fun crypto:sha_mac/2), + + %% Input: + %% P = "password" (8 octets) + %% S = "salt" (4 octets) + %% c = 16777216 + %% dkLen = 20 + + %% Output: + %% DK = ee fe 3d 61 cd 4d a4 e4 + %% e9 94 5b 3d 6b a2 15 8c + %% 26 34 e9 84 (20 octets) + + + <<16#ee, 16#fe, 16#3d, 16#61, 16#cd, 16#4d, 16#a4, 16#e4, + 16#e9, 16#94, 16#5b, 16#3d, 16#6b, 16#a2, 16#15, 16#8c, + 16#26, 16#34, 16#e9, 16#84>> = pubkey_pbe:pbdkdf2("password", "salt", 16777216, 20, 20, fun crypto:sha_mac/2), + + %% Input: + %% P = "passwordPASSWORDpassword" (24 octets) + %% S = "saltSALTsaltSALTsaltSALTsaltSALTsalt" (36 octets) + %% c = 4096 + %% dkLen = 25 + + %% Output: + %% DK = 3d 2e ec 4f e4 1c 84 9b + %% 80 c8 d8 36 62 c0 e4 4a + %% 8b 29 1a 96 4c f2 f0 70 + %% 38 (25 octets) + + <<16#3d, 16#2e, 16#ec, 16#4f, 16#e4, 16#1c, 16#84, 16#9b, + 16#80, 16#c8, 16#d8, 16#36, 16#62, 16#c0, 16#e4, 16#4a, + 16#8b, 16#29, 16#1a, 16#96, 16#4c, 16#f2, 16#f0, 16#70, + 16#38>> + = pubkey_pbe:pbdkdf2("passwordPASSWORDpassword", + "saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, 25, 20, fun crypto:sha_mac/2), + + %% Input: + %% P = "pass\0word" (9 octets) + %% S = "sa\0lt" (5 octets) + %% c = 4096 + %% dkLen = 16 + + %% Output: + %% DK = 56 fa 6a a7 55 48 09 9d + %% cc 37 d7 f0 34 25 e0 c3 (16 octets) + + <<16#56, 16#fa, 16#6a, 16#a7, 16#55, 16#48, 16#09, 16#9d, + 16#cc, 16#37, 16#d7, 16#f0, 16#34, 16#25, 16#e0, 16#c3>> + = pubkey_pbe:pbdkdf2("pass\0word", + "sa\0lt", 4096, 16, 20, fun crypto:sha_mac/2). + + +pbe_des_cbc(doc) -> + ["Tests reading a password DES-CBC encrypted key file"]; +pbe_des_cbc(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + {ok, Pem} = file:read_file(filename:join(Datadir, "des_cbc_enc_key.pem")), + + + PemE = public_key:pem_decode(Pem), + test_server:format("PemE: ~p" , [PemE]), + [{'PrivateKeyInfo', _, _} = PubEntry0] = PemE, + Key = public_key:pem_entry_decode(PubEntry0, "password"), + test_server:format("Key: ~p" , [Key]). + +pbe_des3_ede(doc) -> + ["Tests reading a password DES-CBC encrypted key file"]; +pbe_des3_ede(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + {ok, Pem} = file:read_file(filename:join(Datadir, "des_ede3_cbc_enc_key.pem")), + + PemE = public_key:pem_decode(Pem), + test_server:format("PemE: ~p" , [PemE]), + [{'PrivateKeyInfo', _, _} = PubEntry0] = PemE, + Key = public_key:pem_entry_decode(PubEntry0, "password"), + test_server:format("Key: ~p" , [Key]). diff --git a/lib/public_key/test/pbe_SUITE_data/des_cbc_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/des_cbc_enc_key.pem new file mode 100644 index 0000000000..eaa06145aa --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/des_cbc_enc_key.pem @@ -0,0 +1,11 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBozA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQIfWBDXwLp4K4CAggA +MBEGBSsOAwIHBAiaCF/AvOgQ6QSCAWDWX4BdAzCRNSQSANSuNsT5X8mWYO27mr3Y +9c9LoBVXGNmYWKA77MI4967f7SmjNcgXj3xNE/jmnVz6hhsjS8E5VPT3kfyVkpdZ +0lr5e9Yk2m3JWpPU7++v5zBkZmC4V/MwV/XuIs6U+vykgzMgpxQg0oZKS9zgmiZo +f/4dOCL0UtCDnyOSvqT7mCVIcMDIEKu8QbVlgZYBop08l60EuEU3gARUo8WsYQmO +Dz/ldx0Z+znIT0SXVuOwc+RVItC5T/Qx+aijmmpt+9l14nmaGBrEkmuhmtdvU/4v +aptewGRgmjOfD6cqK+zs0O5NrrJ3P/6ZSxXj91CQgrThGfOv72bUncXEMNtc8pks +2jpHFjGMdKufnadAD7XuMgzkkaklEXZ4f5tU6heIIwr51g0GBEGF96gYPFnjnSQM +75JE02Clo+DfcfXpcybPTwwFg2jd6JTTOfkdf6OdSlA/1XNK43FA +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/public_key/test/pbe_SUITE_data/des_ede3_cbc_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/des_ede3_cbc_enc_key.pem new file mode 100644 index 0000000000..22ea46d56f --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/des_ede3_cbc_enc_key.pem @@ -0,0 +1,11 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBpjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIeFeOWl1jywYCAggA +MBQGCCqGSIb3DQMHBAjUJ5eGBhQGtQSCAWBrHrRgqO8UUMLcWzZEtpk1l3mjxiF/ +koCMkHsFwowgyWhEbgIkTgbSViK54LVK8PskekcGNLph+rB6bGZ7pPbL5pbXASJ8 ++MkQcG3FZdlS4Ek9tTJDApj3O1UubZGFG4uvTlJJFbF1BOJ3MkY3XQ9Gl1qwv7j5 +6e103Da7Cq9+oIDKmznza78XXQYrUsPo8mJGjUxPskEYlzwvHjKubRnYm/K6RKhi +5f4zX4BQ/Dt3H812ZjRXrsjAJP0KrD/jyD/jCT7zNBVPH1izBds+RwizyQAHwfNJ +BFR78TH4cgzB619X47FDVOnT0LqQNVd0O3cSwnPrXE9XR3tPayE+iOB15llFSmi8 +z0ByOXldEpkezCn92Umk++suzIVj1qfsK+bv2phZWJPbLEIWPDRHUbYf76q5ArAr +u4xtxT/hoK3krEs/IN3d70qjlUJ36SEw1UaZ82PWhakQbdtu39ZraMJB +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/public_key/test/pbe_SUITE_data/rc2_cbc_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/rc2_cbc_enc_key.pem new file mode 100644 index 0000000000..618cddcfd7 --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/rc2_cbc_enc_key.pem @@ -0,0 +1,12 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBrjBIBgkqhkiG9w0BBQ0wOzAeBgkqhkiG9w0BBQwwEQQIrHyQPBZqWLUCAggA +AgEQMBkGCCqGSIb3DQMCMA0CAToECEhbh7YZKiPSBIIBYCT1zp6o5jpFlIkgwPop +7bW1+8ACr4exqzkeb3WflQ8cWJ4cURxzVdvxUnXeW1VJdaQZtjS/QHs5GhPTG/0f +wtvnaPfwrIJ3FeGaZfcg2CrYhalOFmEb4xrE4KyoEQmUN8tb/Cg94uzd16BOPw21 +RDnE8bnPdIGY7TyL95kbkqH23mK53pi7h+xWIgduW+atIqDyyt55f7WMZcvDvlj6 +VpN/V0h+qxBHL274WA4dj6GYgeyUFpi60HdGCK7By2TBy8h1ZvKGjmB9h8jZvkx1 +MkbRumXxyFsowTZawyYvO8Um6lbfEDP9zIEUq0IV8RqH2MRyblsPNSikyYhxX/cz +tdDxRKhilySbSBg5Kr8OfcwKp9bpinN96nmG4xr3Tch1bnVvqJzOQ5+Vva2WwVvH +2JkWvYm5WaANg4Q6bRxu9vz7DuhbJjQdZbxFezIAgrJdSe92B00jO/0Kny1WjiVO +6DA= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index b11e4d092a..ddca0c9248 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -23,8 +23,8 @@ %% Note: This directive should only be used in test suites. -compile(export_all). --include_lib("common_test/include/ct.hrl"). --include_lib("test_server/include/test_server_line.hrl"). +%%-include_lib("common_test/include/ct.hrl"). +-include_lib("test_server/include/test_server.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -107,7 +107,7 @@ all() -> {group, ssh_public_key_decode_encode}, encrypt_decrypt, {group, sign_verify}, - pkix, pkix_path_validation, deprecated]. + pkix, pkix_path_validation]. groups() -> [{pem_decode_encode, [], [dsa_pem, rsa_pem, encrypted_pem, @@ -215,19 +215,19 @@ encrypted_pem(Config) when is_list(Config) -> Salt0 = crypto:rand_bytes(8), Entry0 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey, - {{"DES-EDE3-CBC", Salt0}, "1234abcd"}), + {{"DES-EDE3-CBC", {md5, 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}}] = + [{'RSAPrivateKey', _, {"DES-EDE3-CBC", {md5, 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"}), + {{"DES-CBC", {md5, Salt1}}, "4567efgh"}), DesKeyFile = filename:join(Datadir, "des_client_key.pem"), erl_make_certs:der_to_pem(DesKeyFile, [Entry1]), - [{'RSAPrivateKey', _, {"DES-CBC", Salt1}} =Entry2] = + [{'RSAPrivateKey', _, {"DES-CBC", {md5, Salt1}}} =Entry2] = erl_make_certs:pem_to_der(DesKeyFile), true = check_entry_type(public_key:pem_entry_decode(Entry2, "4567efgh"), 'RSAPrivateKey'). @@ -700,23 +700,23 @@ pkix_path_validation(Config) when is_list(Config) -> 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. +%% 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. %%-------------------------------------------------------------------- -- cgit v1.2.3 From b211df3a0cca478822d57836dac3dd6452aab32a Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 18 Oct 2011 18:01:40 +0200 Subject: Additions to crypto and public_key needed for full PKCS-8 support --- lib/public_key/src/pubkey_pbe.erl | 105 +++++++++++++++++++++++--------------- lib/public_key/src/public_key.erl | 1 - lib/public_key/test/pbe_SUITE.erl | 60 ++++++++++++---------- 3 files changed, 97 insertions(+), 69 deletions(-) (limited to 'lib/public_key') diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl index f471871d35..32be347039 100644 --- a/lib/public_key/src/pubkey_pbe.erl +++ b/lib/public_key/src/pubkey_pbe.erl @@ -27,35 +27,43 @@ -define(DEFAULT_SHA_MAC_KEYLEN, 20). --define(OCTET_STR, 4). --define(IV_LEN, 8). +-define(ASN1_OCTET_STR_TAG, 4). +-define(IV_LEN, 8). %%==================================================================== %% Internal application API %%==================================================================== -pbdkdf2(Password, Salt, Count, DerivedKeyLen, PrfLen, Prf)-> - NumBlocks = ceiling(DerivedKeyLen / PrfLen), - NumLastBlockOctets = DerivedKeyLen - (NumBlocks - 1) * PrfLen , - blocks(NumBlocks, NumLastBlockOctets, 1, Password, Salt, Count, Prf, PrfLen, <<>>). +pbdkdf2(Password, Salt, Count, DerivedKeyLen, Prf, PrfOutputLen)-> + NumBlocks = ceiling(DerivedKeyLen / PrfOutputLen), + NumLastBlockOctets = DerivedKeyLen - (NumBlocks - 1) * PrfOutputLen , + blocks(NumBlocks, NumLastBlockOctets, 1, Password, Salt, Count, Prf, PrfOutputLen, <<>>). encode(Data, Password, "DES-CBC" = Cipher, KeyDevParams) -> - {Key, IV} = password_to_key_and_iv(Password, derived_key_length(Cipher), KeyDevParams), + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), crypto:des_cbc_encrypt(Key, IV, Data); encode(Data, Password, "DES-EDE3-CBC" = Cipher, KeyDevParams) -> - {Key, IV} = password_to_key_and_iv(Password, derived_key_length(Cipher), KeyDevParams), + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), <> = Key, - crypto:des_ede3_cbc_encrypt(Key1, Key2, Key3, IV, Data). + crypto:des_ede3_cbc_encrypt(Key1, Key2, Key3, IV, Data); + +encode(Data, Password, "RC2-CBC" = Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:rc2_cbc_encrypt(Key, IV, Data). decode(Data, Password,"DES-CBC"= Cipher, KeyDevParams) -> - {Key, IV} = password_to_key_and_iv(Password, derived_key_length(Cipher), KeyDevParams), + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), crypto:des_cbc_decrypt(Key, IV, Data); decode(Data, Password,"DES-EDE3-CBC" = Cipher, KeyDevParams) -> - {Key, IV} = password_to_key_and_iv(Password, derived_key_length(Cipher), KeyDevParams), + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), <> = Key, - crypto:des_ede3_cbc_decrypt(Key1, Key2, Key3, IV, Data). + crypto:des_ede3_cbc_decrypt(Key1, Key2, Key3, IV, Data); + +decode(Data, Password,"RC2-CBC"= Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:rc2_cbc_decrypt(Key, IV, Data). %%-------------------------------------------------------------------- -spec pbdkdf1(string(), iodata(), integer(), atom()) -> binary(). @@ -94,13 +102,20 @@ decrypt_parameters(#'EncryptedPrivateKeyInfo_encryptionAlgorithm'{ %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -password_to_key_and_iv(Password, KeyLen, {salt, Salt}) -> +password_to_key_and_iv(Password, Cipher, {salt, Salt}) -> + KeyLen = derived_key_length(Cipher, undefined), <> = pem_encrypt(<<>>, Password, Salt, ceiling(KeyLen div 16), <<>>, md5), %% Old PEM encryption does not use standard encryption method %% pbdkdf1 and uses then salt as IV {Key, Salt}. +password_to_key_and_iv(Password, _, #'PBES2-params'{} = Params) -> + {Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoOtputLen, IV} = + key_derivation_params(Params), + <> = + pbdkdf2(Password, Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoOtputLen), + {Key, IV}. pem_encrypt(_, _, _, 0, Acc, _) -> Acc; pem_encrypt(Prev, Password, Salt, Count, Acc, Hash) -> @@ -118,8 +133,12 @@ iv(#'PBES2-params_encryptionScheme'{algorithm = Algo, (Algo == ?'des-EDE3-CBC') -> %% This is an so called open ASN1-type that in this %% case will be an octet-string of length 8 - <> = ASNIV, - IV. + <> = ASNIV, + IV; +iv(#'PBES2-params_encryptionScheme'{algorithm = ?'rc2CBC', + parameters = ASN1IV}) -> + {ok, #'RC2-CBC-Parameter'{iv = IV}} = 'PKCS-FRAME':decode('RC2-CBC-Parameter', ASN1IV), + iolist_to_binary(IV). blocks(1, N, Index, Password, Salt, Count, Prf, PrfLen, Acc) -> <> = xor_sum(Password, Salt, Count, Index, Prf, PrfLen), @@ -129,53 +148,55 @@ blocks(NumBlocks, N, Index, Password, Salt, Count, Prf, PrfLen, Acc) -> blocks(NumBlocks -1, N, Index +1, Password, Salt, Count, Prf, PrfLen, <>). xor_sum(Password, Salt, Count, Index, Prf, PrfLen) -> - %%Result = Prf(Password, [Salt,<>], PrfLen), - Result = Prf(Password, [Salt,<>]), + Result = Prf(Password, [Salt,<>], PrfLen), do_xor_sum(Prf, PrfLen, Result, Password, Count-1, Result). do_xor_sum(_, _, _, _, 0, Acc) -> Acc; do_xor_sum(Prf, PrfLen, Prev, Password, Count, Acc)-> - %%Result = Prf(Password, Prev, PrfLen), - Result = Prf(Password, Prev), + Result = Prf(Password, Prev, PrfLen), do_xor_sum(Prf, PrfLen, Result, Password, Count-1, crypto:exor(Acc, Result)). decrypt_parameters(?'id-PBES2', DekParams) -> {ok, Params} = 'PKCS-FRAME':decode('PBES2-params', DekParams), {cipher(Params#'PBES2-params'.encryptionScheme), Params}. -key_derivation_params(#'PBES2-params_keyDerivationFunc'{algorithm = ?'id-PBKDF2', - parameters = - #'PBKDF2-params'{salt = {specified, OctetSalt}, - iterationCount = Count, - keyLength = Length, - prf = Prf}}) -> - PseudoRandomFunction = pseudo_random_function(Prf), - KeyLen = pseudo_key_length(Length, Prf), - {iolist_to_binary(OctetSalt), Count, KeyLen, PseudoRandomFunction}. - +key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc, + encryptionScheme = EncScheme}) -> + #'PBES2-params_keyDerivationFunc'{algorithm = ?'id-PBKDF2', + parameters = + #'PBKDF2-params'{salt = {specified, OctetSalt}, + iterationCount = Count, + keyLength = Length, + prf = Prf}} = KeyDerivationFunc, + #'PBES2-params_encryptionScheme'{algorithm = Algo} = EncScheme, + {PseudoRandomFunction, PseudoOtputLen} = pseudo_random_function(Prf), + KeyLen = derived_key_length(Algo, Length), + {iolist_to_binary(OctetSalt), Count, KeyLen, PseudoRandomFunction, PseudoOtputLen, iv(EncScheme)}. + +%% This function currently matches a tuple that ougth to be the value ?'id-hmacWithSHA1, +%% but we need some kind of ASN1-fix for this. pseudo_random_function(#'PBKDF2-params_prf'{algorithm = {_,_, _,'id-hmacWithSHA1'}}) -> - %%fun crypto:sha_mac_n/3. - fun crypto:sha_mac/2. + {fun crypto:sha_mac/3, pseudo_output_length(?'id-hmacWithSHA1')}. -pseudo_key_length(asn1_NOVALUE, #'PBKDF2-params_prf'{algorithm = {_,_, _,'id-hmacWithSHA1'}}) -> - ?DEFAULT_SHA_MAC_KEYLEN; -pseudo_key_length(Len, _) when is_integer(Len) -> - Len. +pseudo_output_length(?'id-hmacWithSHA1') -> + ?DEFAULT_SHA_MAC_KEYLEN. -derived_key_length("DES-CBC") -> +derived_key_length(_, Len) when is_integer(Len) -> + Len; +derived_key_length(Cipher,_) when (Cipher == ?'desCBC') or (Cipher == "DES-CBC") -> 8; -%% derived_key_length("RC2-CBC") -> -%% 5; -derived_key_length("DES-EDE3-CBC") -> +derived_key_length(Cipher,_) when (Cipher == ?'rc2CBC') or (Cipher == "RC2-CBC") -> + 16; +derived_key_length(Cipher,_) when (Cipher == ?'des-EDE3-CBC') or (Cipher == "DES-EDE3-CBC") -> 24. cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'desCBC'}) -> "DES-CBC"; cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'des-EDE3-CBC'}) -> - "DES-EDE3-CBC". -%% cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'rc2CBC'}) -> -%% "RC2-CBC". + "DES-EDE3-CBC"; +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'rc2CBC'}) -> + "RC2-CBC". ceiling(Float) -> erlang:round(Float + 0.5). diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 68c7b7ad93..19465e7828 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -154,7 +154,6 @@ der_decode(Asn1Type, Der) when (Asn1Type == 'PrivateKeyInfo') or (Asn1Type == ' andalso is_binary(Der) -> try {ok, Decoded} = 'PKCS-FRAME':decode(Asn1Type, Der), - Decoded catch error:{badmatch, {error, _}} = Error -> diff --git a/lib/public_key/test/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl index 1d33976505..8dc9a01529 100644 --- a/lib/public_key/test/pbe_SUITE.erl +++ b/lib/public_key/test/pbe_SUITE.erl @@ -20,6 +20,7 @@ -module(pbe_SUITE). -include_lib("test_server/include/test_server.hrl"). +-include_lib("public_key/include/public_key.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -157,7 +158,7 @@ pbdkdf2(Config) when is_list(Config) -> <<16#ea, 16#6c, 16#01, 16#4d, 16#c7, 16#2d, 16#6f, 16#8c, 16#cd, 16#1e, 16#d9, 16#2a, 16#ce, 16#1d, 16#41, 16#f0, 16#d8, 16#de, 16#89, 16#57>> = - pubkey_pbe:pbdkdf2("password", "salt", 2, 20, 20, fun crypto:sha_mac/2), + pubkey_pbe:pbdkdf2("password", "salt", 2, 20, fun crypto:sha_mac/3, 20), %% Input: %% P = "password" (8 octets) @@ -172,7 +173,7 @@ pbdkdf2(Config) when is_list(Config) -> <<16#4b, 16#00, 16#79, 16#01, 16#b7, 16#65, 16#48, 16#9a, 16#be, 16#ad, 16#49, 16#d9, 16#26, 16#f7, 16#21, 16#d0, - 16#65, 16#a4, 16#29, 16#c1>> = pubkey_pbe:pbdkdf2("password", "salt", 4096, 20, 20, fun crypto:sha_mac/2), + 16#65, 16#a4, 16#29, 16#c1>> = pubkey_pbe:pbdkdf2("password", "salt", 4096, 20, fun crypto:sha_mac/3, 20), %% Input: %% P = "password" (8 octets) @@ -188,7 +189,7 @@ pbdkdf2(Config) when is_list(Config) -> <<16#ee, 16#fe, 16#3d, 16#61, 16#cd, 16#4d, 16#a4, 16#e4, 16#e9, 16#94, 16#5b, 16#3d, 16#6b, 16#a2, 16#15, 16#8c, - 16#26, 16#34, 16#e9, 16#84>> = pubkey_pbe:pbdkdf2("password", "salt", 16777216, 20, 20, fun crypto:sha_mac/2), + 16#26, 16#34, 16#e9, 16#84>> = pubkey_pbe:pbdkdf2("password", "salt", 16777216, 20, fun crypto:sha_mac/3, 20), %% Input: %% P = "passwordPASSWORDpassword" (24 octets) @@ -207,7 +208,7 @@ pbdkdf2(Config) when is_list(Config) -> 16#8b, 16#29, 16#1a, 16#96, 16#4c, 16#f2, 16#f0, 16#70, 16#38>> = pubkey_pbe:pbdkdf2("passwordPASSWORDpassword", - "saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, 25, 20, fun crypto:sha_mac/2), + "saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, 25, fun crypto:sha_mac/3, 20), %% Input: %% P = "pass\0word" (9 octets) @@ -222,30 +223,37 @@ pbdkdf2(Config) when is_list(Config) -> <<16#56, 16#fa, 16#6a, 16#a7, 16#55, 16#48, 16#09, 16#9d, 16#cc, 16#37, 16#d7, 16#f0, 16#34, 16#25, 16#e0, 16#c3>> = pubkey_pbe:pbdkdf2("pass\0word", - "sa\0lt", 4096, 16, 20, fun crypto:sha_mac/2). + "sa\0lt", 4096, 16, fun crypto:sha_mac/3, 20). - -pbe_des_cbc(doc) -> - ["Tests reading a password DES-CBC encrypted key file"]; -pbe_des_cbc(Config) when is_list(Config) -> +encrypted_private_key_info(doc) -> + ["Tests reading a EncryptedPrivateKeyInfo file different ciphers"]; +encrypted_private_key_info(Config) when is_list(Config) -> Datadir = ?config(data_dir, Config), - {ok, Pem} = file:read_file(filename:join(Datadir, "des_cbc_enc_key.pem")), + {ok, PemDes} = file:read_file(filename:join(Datadir, "des_cbc_enc_key.pem")), + PemDesEntry = public_key:pem_decode(PemDes), + test_server:format("Pem entry: ~p" , [PemDesEntry]), + [{'PrivateKeyInfo', _, {"DES-CBC",_}} = PubEntry0] = PemDesEntry, + KeyInfo = public_key:pem_entry_decode(PubEntry0, "password"), - PemE = public_key:pem_decode(Pem), - test_server:format("PemE: ~p" , [PemE]), - [{'PrivateKeyInfo', _, _} = PubEntry0] = PemE, - Key = public_key:pem_entry_decode(PubEntry0, "password"), - test_server:format("Key: ~p" , [Key]). - -pbe_des3_ede(doc) -> - ["Tests reading a password DES-CBC encrypted key file"]; -pbe_des3_ede(Config) when is_list(Config) -> - Datadir = ?config(data_dir, Config), - {ok, Pem} = file:read_file(filename:join(Datadir, "des_ede3_cbc_enc_key.pem")), + {ok, Pem3Des} = file:read_file(filename:join(Datadir, "des_ede3_cbc_enc_key.pem")), + + Pem3DesEntry = public_key:pem_decode(Pem3Des), + test_server:format("Pem entry: ~p" , [Pem3DesEntry]), + [{'PrivateKeyInfo', _, {"DES-EDE3-CBC",_}} = PubEntry1] = Pem3DesEntry, + KeyInfo = public_key:pem_entry_decode(PubEntry1, "password"), + + {ok, PemRc2} = file:read_file(filename:join(Datadir, "rc2_cbc_enc_key.pem")), + + PemRc2Entry = public_key:pem_decode(PemRc2), + test_server:format("Pem entry: ~p" , [PemRc2Entry]), + [{'PrivateKeyInfo', _, {"RC2-CBC",_}} = PubEntry2] = PemRc2Entry, + KeyInfo = public_key:pem_entry_decode(PubEntry2, "password"), + + check_key_info(KeyInfo). + - PemE = public_key:pem_decode(Pem), - test_server:format("PemE: ~p" , [PemE]), - [{'PrivateKeyInfo', _, _} = PubEntry0] = PemE, - Key = public_key:pem_entry_decode(PubEntry0, "password"), - test_server:format("Key: ~p" , [Key]). +check_key_info(#'PrivateKeyInfo'{privateKeyAlgorithm = + #'PrivateKeyInfo_privateKeyAlgorithm'{algorithm = ?rsaEncryption}, + privateKey = Key}) -> + #'RSAPrivateKey'{} = public_key:der_decode('RSAPrivateKey', iolist_to_binary(Key)). -- cgit v1.2.3 From 68e803093b8bf43b39f3090a1b717b850a5e8f4b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 24 Oct 2011 15:01:28 +0200 Subject: Clean up of public_key code adding specs and documentation --- lib/public_key/asn1/Makefile | 4 +- lib/public_key/doc/src/public_key.xml | 23 ++++++--- lib/public_key/src/pubkey_pbe.erl | 57 ++++++++++++--------- lib/public_key/src/pubkey_pem.erl | 63 ++++------------------- lib/public_key/src/public_key.erl | 85 +++++++++++++++----------------- lib/public_key/test/pbe_SUITE.erl | 2 +- lib/public_key/test/public_key_SUITE.erl | 29 ++--------- 7 files changed, 103 insertions(+), 160 deletions(-) (limited to 'lib/public_key') diff --git a/lib/public_key/asn1/Makefile b/lib/public_key/asn1/Makefile index e56e7e73ad..2ce1168349 100644 --- a/lib/public_key/asn1/Makefile +++ b/lib/public_key/asn1/Makefile @@ -82,8 +82,8 @@ docs: %.erl %.hrl: %.set.asn erlc $(ASN_FLAGS) $< -$(HRL_FILES): $(ASN_HRLS) - cp -p $^ $(INCLUDE) +$(INCLUDE)/%.hrl: %.hrl + cp -p $< $@ # ---------------------------------------------------- # Release Target diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index 9a3832c68b..821e7a2300 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -61,11 +61,14 @@

string = [bytes()]

pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey' - 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'

+ 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'| 'PrivateKeyInfo'

pem_entry () = {pki_asn1_type(), binary(), %% DER or encrypted DER - not_encrypted | {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}}.

- + not_encrypted | cipher_info()}

+ +

cipher_info() = {"RC2-CBC | "DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)} | + 'PBES2-params'}

+

rsa_public_key() = #'RSAPublicKey'{}

rsa_private_key() = #'RSAPrivateKey'{}

@@ -118,7 +121,8 @@ - decrypt_private(CipherText, Key [, Options]) -> binary() + decrypt_private(CipherText, Key) -> binary() + decrypt_private(CipherText, Key, Options) -> binary() Public key decryption. CipherText = binary() @@ -131,7 +135,8 @@ - decrypt_public(CipherText, Key [, Options]) - > binary() + decrypt_public(CipherText, Key) - > binary() + decrypt_public(CipherText, Key, Options) - > binary() CipherText = binary() @@ -198,7 +203,8 @@ - pem_entry_decode(PemEntry [, Password]) -> term() + pem_entry_decode(PemEntry) -> term() + pem_entry_decode(PemEntry, Password) -> term() Decodes a pem entry. PemEntry = pem_entry() @@ -213,7 +219,8 @@ - pem_entry_encode(Asn1Type, Entity [,{CipherInfo, Password}]) -> pem_entry() + pem_entry_encode(Asn1Type, Entity) -> pem_entry() + pem_entry_encode(Asn1Type, Entity, {CipherInfo, Password}) -> pem_entry() Creates a pem entry that can be fed to pem_encode/1. Asn1Type = pki_asn1_type() @@ -224,7 +231,7 @@ dsa_public_key() and this function will create the appropriate 'SubjectPublicKeyInfo' entry. - CipherInfo = {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)} + CipherInfo = cipher_info() Password = string() diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl index 32be347039..77d6943d96 100644 --- a/lib/public_key/src/pubkey_pbe.erl +++ b/lib/public_key/src/pubkey_pbe.erl @@ -26,7 +26,6 @@ -export([pbdkdf1/4, pbdkdf2/6]). -define(DEFAULT_SHA_MAC_KEYLEN, 20). - -define(ASN1_OCTET_STR_TAG, 4). -define(IV_LEN, 8). @@ -34,11 +33,11 @@ %% Internal application API %%==================================================================== -pbdkdf2(Password, Salt, Count, DerivedKeyLen, Prf, PrfOutputLen)-> - NumBlocks = ceiling(DerivedKeyLen / PrfOutputLen), - NumLastBlockOctets = DerivedKeyLen - (NumBlocks - 1) * PrfOutputLen , - blocks(NumBlocks, NumLastBlockOctets, 1, Password, Salt, Count, Prf, PrfOutputLen, <<>>). - +%%-------------------------------------------------------------------- +-spec encode(binary(), string(), string(), term()) -> binary(). +%% +%% Description: Performs password based encoding +%%-------------------------------------------------------------------- encode(Data, Password, "DES-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), crypto:des_cbc_encrypt(Key, IV, Data); @@ -51,7 +50,11 @@ encode(Data, Password, "DES-EDE3-CBC" = Cipher, KeyDevParams) -> encode(Data, Password, "RC2-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), crypto:rc2_cbc_encrypt(Key, IV, Data). - +%%-------------------------------------------------------------------- +-spec decode(binary(), string(), string(), term()) -> binary(). +%% +%% Description: Performs password based decoding +%%-------------------------------------------------------------------- decode(Data, Password,"DES-CBC"= Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), crypto:des_cbc_decrypt(Key, IV, Data); @@ -102,7 +105,13 @@ decrypt_parameters(#'EncryptedPrivateKeyInfo_encryptionAlgorithm'{ %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -password_to_key_and_iv(Password, Cipher, {salt, Salt}) -> +password_to_key_and_iv(Password, _, #'PBES2-params'{} = Params) -> + {Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoOtputLen, IV} = + key_derivation_params(Params), + <> = + pbdkdf2(Password, Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoOtputLen), + {Key, IV}; +password_to_key_and_iv(Password, Cipher, Salt) -> KeyLen = derived_key_length(Cipher, undefined), <> = pem_encrypt(<<>>, Password, Salt, ceiling(KeyLen div 16), <<>>, md5), @@ -110,12 +119,6 @@ password_to_key_and_iv(Password, Cipher, {salt, Salt}) -> %% pbdkdf1 and uses then salt as IV {Key, Salt}. -password_to_key_and_iv(Password, _, #'PBES2-params'{} = Params) -> - {Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoOtputLen, IV} = - key_derivation_params(Params), - <> = - pbdkdf2(Password, Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoOtputLen), - {Key, IV}. pem_encrypt(_, _, _, 0, Acc, _) -> Acc; pem_encrypt(Prev, Password, Salt, Count, Acc, Hash) -> @@ -129,8 +132,8 @@ do_pbdkdf1(Prev, Count, Acc, Hash) -> do_pbdkdf1(Result, Count-1 , <>, Hash). iv(#'PBES2-params_encryptionScheme'{algorithm = Algo, - parameters = ASNIV}) when (Algo == ?'desCBC') or - (Algo == ?'des-EDE3-CBC') -> + parameters = ASNIV}) when (Algo == ?'desCBC') or + (Algo == ?'des-EDE3-CBC') -> %% This is an so called open ASN1-type that in this %% case will be an octet-string of length 8 <> = ASNIV, @@ -145,7 +148,8 @@ blocks(1, N, Index, Password, Salt, Count, Prf, PrfLen, Acc) -> <>; blocks(NumBlocks, N, Index, Password, Salt, Count, Prf, PrfLen, Acc) -> XorSum = xor_sum(Password, Salt, Count, Index, Prf, PrfLen), - blocks(NumBlocks -1, N, Index +1, Password, Salt, Count, Prf, PrfLen, <>). + blocks(NumBlocks -1, N, Index +1, Password, Salt, Count, Prf, + PrfLen, <>). xor_sum(Password, Salt, Count, Index, Prf, PrfLen) -> Result = Prf(Password, [Salt,<>], PrfLen), @@ -172,11 +176,13 @@ key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc, #'PBES2-params_encryptionScheme'{algorithm = Algo} = EncScheme, {PseudoRandomFunction, PseudoOtputLen} = pseudo_random_function(Prf), KeyLen = derived_key_length(Algo, Length), - {iolist_to_binary(OctetSalt), Count, KeyLen, PseudoRandomFunction, PseudoOtputLen, iv(EncScheme)}. + {OctetSalt, Count, KeyLen, + PseudoRandomFunction, PseudoOtputLen, iv(EncScheme)}. -%% This function currently matches a tuple that ougth to be the value ?'id-hmacWithSHA1, -%% but we need some kind of ASN1-fix for this. -pseudo_random_function(#'PBKDF2-params_prf'{algorithm = {_,_, _,'id-hmacWithSHA1'}}) -> +%% This function currently matches a tuple that ougth to be the value +%% ?'id-hmacWithSHA1, but we need some kind of ASN1-fix for this. +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = + {_,_, _,'id-hmacWithSHA1'}}) -> {fun crypto:sha_mac/3, pseudo_output_length(?'id-hmacWithSHA1')}. pseudo_output_length(?'id-hmacWithSHA1') -> @@ -184,11 +190,14 @@ pseudo_output_length(?'id-hmacWithSHA1') -> derived_key_length(_, Len) when is_integer(Len) -> Len; -derived_key_length(Cipher,_) when (Cipher == ?'desCBC') or (Cipher == "DES-CBC") -> +derived_key_length(Cipher,_) when (Cipher == ?'desCBC') or + (Cipher == "DES-CBC") -> 8; -derived_key_length(Cipher,_) when (Cipher == ?'rc2CBC') or (Cipher == "RC2-CBC") -> +derived_key_length(Cipher,_) when (Cipher == ?'rc2CBC') or + (Cipher == "RC2-CBC") -> 16; -derived_key_length(Cipher,_) when (Cipher == ?'des-EDE3-CBC') or (Cipher == "DES-EDE3-CBC") -> +derived_key_length(Cipher,_) when (Cipher == ?'des-EDE3-CBC') or + (Cipher == "DES-EDE3-CBC") -> 24. cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'desCBC'}) -> diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl index f19aab0533..910473d629 100644 --- a/lib/public_key/src/pubkey_pem.erl +++ b/lib/public_key/src/pubkey_pem.erl @@ -43,8 +43,6 @@ -include("public_key.hrl"). -export([encode/1, decode/1, decipher/2, cipher/3]). -%% Backwards compatibility -%%-export([decode_key/2]). -define(ENCODED_LINE_LENGTH, 64). @@ -69,26 +67,22 @@ encode(PemEntries) -> encode_pem_entries(PemEntries). %%-------------------------------------------------------------------- --spec decipher({pki_asn1_type(), DerEncrypted::binary(), term()}, - %%{Cipher :: string(), - %%Salt :: binary()}}, - string()) -> Der::binary(). +-spec decipher({pki_asn1_type(), DerEncrypted::binary(), + {Cipher :: string(), Salt :: iodata() | #'PBES2-params'{}}}, + string()) -> Der::binary(). %% %% Description: Deciphers a decrypted pem entry. %%-------------------------------------------------------------------- decipher({_, DecryptDer, {Cipher, KeyDevParams}}, Password) -> - %%decode_key(DecryptDer, Password, Cipher, Salt). pubkey_pbe:decode(DecryptDer, Password, Cipher, KeyDevParams). %%-------------------------------------------------------------------- --spec cipher(Der::binary(), term(), -%%{Cipher :: string(), Hash::atom(), Salt :: binary()} , +-spec cipher(Der::binary(), {Cipher :: string(), Salt :: iodata() | #'PBES2-params'{}} , string()) -> binary(). %% %% Description: Ciphers a PEM entry %%-------------------------------------------------------------------- cipher(Der, {Cipher, KeyDevParams}, Password)-> - %%encode_key(Der, Password, Cipher, Salt). pubkey_pbe:encode(Der, Password, Cipher, KeyDevParams). %%-------------------------------------------------------------------- @@ -100,7 +94,7 @@ encode_pem_entries(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}}}) -> +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"]. @@ -126,7 +120,7 @@ decode_pem_entry(Start, [<<"Proc-Type: 4,ENCRYPTED", _/binary>>, Line | Lines]) Decoded = base64:mime_decode(Cs), [_, DekInfo0] = string:tokens(binary_to_list(Line), ": "), [Cipher, Salt] = string:tokens(DekInfo0, ","), - {Type, Decoded, {Cipher, {salt, unhex(Salt)}}}; + {Type, Decoded, {Cipher, unhex(Salt)}}; decode_pem_entry(Start, Lines) -> Type = asn1_type(Start), Cs = erlang:iolist_to_binary(Lines), @@ -140,9 +134,9 @@ decode_pem_entry(Start, Lines) -> decode_encrypted_private_keyinfo(Der) -> #'EncryptedPrivateKeyInfo'{encryptionAlgorithm = AlgorithmInfo, - encryptedData = Data} = public_key:der_decode('EncryptedPrivateKeyInfo', Der), - DecryptParams = pubkey_pbe:decrypt_parameters(AlgorithmInfo), - + encryptedData = Data} = + public_key:der_decode('EncryptedPrivateKeyInfo', Der), + DecryptParams = pubkey_pbe:decrypt_parameters(AlgorithmInfo), {'PrivateKeyInfo', iolist_to_binary(Data), DecryptParams}. split_bin(Bin) -> @@ -176,37 +170,6 @@ join_entry([<<"-----END ", _/binary>>| Lines], Entry) -> join_entry([Line | Lines], Entry) -> join_entry(Lines, [Line | Entry]). -%% decode_key(Data, Password, "DES-CBC", Salt) -> -%% Key = password_to_key(Password, Salt, 8), -%% IV = Salt, -%% crypto:des_cbc_decrypt(Key, IV, Data); -%% decode_key(Data, Password, "DES-EDE3-CBC", Salt) -> -%% Key = password_to_key(Password, Salt, 24), -%% IV = Salt, -%% <> = 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, -%% <> = Key, -%% crypto:des_ede3_cbc_encrypt(Key1, Key2, Key3, IV, Data). - -%% password_to_key(Data, Salt, KeyLen) -> -%% <> = -%% password_to_key(<<>>, Data, Salt, KeyLen, <<>>), -%% Key. - -%% password_to_key(_, _, _, Len, Acc) when Len =< 0 -> -%% Acc; -%% password_to_key(Prev, Data, Salt, Len, Acc) -> -%% M = crypto:md5([Prev, Data, Salt]), -%% password_to_key(M, Data, Salt, Len - size(M), <>). - unhex(S) -> unhex(S, []). @@ -273,11 +236,3 @@ pem_decrypt() -> 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/public_key.erl b/lib/public_key/src/public_key.erl index 19465e7828..753322b46d 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -45,13 +45,6 @@ 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()}]. @@ -104,20 +97,23 @@ pem_entry_decode({Asn1Type, Der, not_encrypted}) when is_atom(Asn1Type), pem_entry_decode({Asn1Type, Der, not_encrypted}, _) when is_atom(Asn1Type), is_binary(Der) -> der_decode(Asn1Type, Der); -pem_entry_decode({Asn1Type, CryptDer, {Cipher, _Params}} = PemEntry, - Password) when is_atom(Asn1Type), - is_binary(CryptDer), +pem_entry_decode({Asn1Type, CryptDer, {Cipher, #'PBES2-params'{}}} = PemEntry, + Password) when is_atom(Asn1Type) andalso + is_binary(CryptDer) andalso is_list(Cipher) -> - Der = pubkey_pem:decipher(PemEntry, Password), - der_decode(Asn1Type, Der). + do_pem_entry_decode(PemEntry, Password); +pem_entry_decode({Asn1Type, CryptDer, {Cipher, Salt}} = PemEntry, + Password) when is_atom(Asn1Type) andalso + is_binary(CryptDer) andalso + is_list(Cipher) andalso + is_binary(Salt) andalso + erlang:byte_size(Salt) == 8 -> + do_pem_entry_decode(PemEntry, Password). %%-------------------------------------------------------------------- -spec pem_entry_encode(pki_asn1_type(), term()) -> pem_entry(). --spec pem_entry_encode(pki_asn1_type(), term(), - %%{{Cipher :: string(), Salt :: binary()}, string()} - term()) -> - pem_entry(). - % +-spec pem_entry_encode(pki_asn1_type(), term(), term()) -> pem_entry(). +%% %% Description: Creates a pem entry that can be feed to pem_encode/1. %%-------------------------------------------------------------------- pem_entry_encode('SubjectPublicKeyInfo', Entity=#'RSAPublicKey'{}) -> @@ -135,22 +131,27 @@ pem_entry_encode('SubjectPublicKeyInfo', pem_entry_encode(Asn1Type, Entity) when is_atom(Asn1Type) -> Der = der_encode(Asn1Type, Entity), {Asn1Type, Der, not_encrypted}. -pem_entry_encode(Asn1Type, Entity, {CipherInfo, Password}) when is_atom(Asn1Type), - %%is_list(Cipher), - %%is_binary(Salt), - %%is_atom(Hash), - %% erlang:byte_size(Salt) == 8, - is_list(Password)-> - Der = der_encode(Asn1Type, Entity), - DecryptDer = pubkey_pem:cipher(Der, CipherInfo, Password), - {Asn1Type, DecryptDer, CipherInfo}. - +pem_entry_encode(Asn1Type, Entity, {{Cipher, #'PBES2-params'{}} = CipherInfo, + Password}) when is_atom(Asn1Type) andalso + is_list(Password) andalso + is_list(Cipher) -> + do_pem_entry_encode(Asn1Type, Entity, CipherInfo, Password); + +pem_entry_encode(Asn1Type, Entity, {{Cipher, Salt} = CipherInfo, + Password}) when is_atom(Asn1Type) andalso + is_list(Password) andalso + is_list(Cipher) andalso + is_binary(Salt) andalso + erlang:byte_size(Salt) == 8 -> + do_pem_entry_encode(Asn1Type, Entity, CipherInfo, Password). + %%-------------------------------------------------------------------- -spec der_decode(asn1_type(), Der::binary()) -> term(). %% %% Description: Decodes a public key asn1 der encoded entity. %%-------------------------------------------------------------------- -der_decode(Asn1Type, Der) when (Asn1Type == 'PrivateKeyInfo') or (Asn1Type == 'EncryptedPrivateKeyInfo') +der_decode(Asn1Type, Der) when (Asn1Type == 'PrivateKeyInfo') or + (Asn1Type == 'EncryptedPrivateKeyInfo') andalso is_binary(Der) -> try {ok, Decoded} = 'PKCS-FRAME':decode(Asn1Type, Der), @@ -174,7 +175,8 @@ der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) -> %% %% Description: Encodes a public key entity with asn1 DER encoding. %%-------------------------------------------------------------------- -der_encode(Asn1Type, Entity) when Asn1Type == 'PrivateKeyInfo'; Asn1Type == 'EncryptedPrivateKeyInfo' -> +der_encode(Asn1Type, Entity) when (Asn1Type == 'PrivateKeyInfo') or + (Asn1Type == 'EncryptedPrivateKeyInfo') -> try {ok, Encoded} = 'PKCS-FRAME':encode(Asn1Type, Entity), iolist_to_binary(Encoded) @@ -552,6 +554,14 @@ ssh_encode(Entries, Type) when is_list(Entries), %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +do_pem_entry_encode(Asn1Type, Entity, CipherInfo, Password) -> + Der = der_encode(Asn1Type, Entity), + DecryptDer = pubkey_pem:cipher(Der, CipherInfo, Password), + {Asn1Type, DecryptDer, CipherInfo}. + +do_pem_entry_decode({Asn1Type,_, _} = PemEntry, Password) -> + Der = pubkey_pem:decipher(PemEntry, Password), + der_decode(Asn1Type, Der). encrypt_public(PlainText, N, E, Options)-> Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), @@ -649,20 +659,3 @@ validate(DerCert, #path_validation_state{working_issuer_name = Issuer, sized_binary(Binary) -> Size = size(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/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl index 8dc9a01529..380a67db7b 100644 --- a/lib/public_key/test/pbe_SUITE.erl +++ b/lib/public_key/test/pbe_SUITE.erl @@ -226,7 +226,7 @@ pbdkdf2(Config) when is_list(Config) -> "sa\0lt", 4096, 16, fun crypto:sha_mac/3, 20). encrypted_private_key_info(doc) -> - ["Tests reading a EncryptedPrivateKeyInfo file different ciphers"]; + ["Tests reading a EncryptedPrivateKeyInfo file encrypted with different ciphers"]; encrypted_private_key_info(Config) when is_list(Config) -> Datadir = ?config(data_dir, Config), {ok, PemDes} = file:read_file(filename:join(Datadir, "des_cbc_enc_key.pem")), diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index ddca0c9248..a91dcfa029 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -215,19 +215,19 @@ encrypted_pem(Config) when is_list(Config) -> Salt0 = crypto:rand_bytes(8), Entry0 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey, - {{"DES-EDE3-CBC", {md5, Salt0}}, "1234abcd"}), + {{"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", {md5, Salt0}}}] = + [{'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", {md5, Salt1}}, "4567efgh"}), + {{"DES-CBC", Salt1}, "4567efgh"}), DesKeyFile = filename:join(Datadir, "des_client_key.pem"), erl_make_certs:der_to_pem(DesKeyFile, [Entry1]), - [{'RSAPrivateKey', _, {"DES-CBC", {md5, Salt1}}} =Entry2] = + [{'RSAPrivateKey', _, {"DES-CBC", Salt1}} =Entry2] = erl_make_certs:pem_to_der(DesKeyFile), true = check_entry_type(public_key:pem_entry_decode(Entry2, "4567efgh"), 'RSAPrivateKey'). @@ -699,27 +699,6 @@ pkix_path_validation(Config) when is_list(Config) -> 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') -> -- cgit v1.2.3 From 0eeda739555deda1c7db37ae68b324230d3d9be9 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 11 Nov 2011 10:46:37 +0100 Subject: Add clause for expected input to pubkey:pseudo_random_function/2 when ASN-1 compiler is fixed. --- lib/public_key/src/pubkey_pbe.erl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/public_key') diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl index 77d6943d96..43f6c42f10 100644 --- a/lib/public_key/src/pubkey_pbe.erl +++ b/lib/public_key/src/pubkey_pbe.erl @@ -183,6 +183,8 @@ key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc, %% ?'id-hmacWithSHA1, but we need some kind of ASN1-fix for this. pseudo_random_function(#'PBKDF2-params_prf'{algorithm = {_,_, _,'id-hmacWithSHA1'}}) -> + {fun crypto:sha_mac/3, pseudo_output_length(?'id-hmacWithSHA1')}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA1'}) -> {fun crypto:sha_mac/3, pseudo_output_length(?'id-hmacWithSHA1')}. pseudo_output_length(?'id-hmacWithSHA1') -> -- cgit v1.2.3