From f9782490288ff3f2510e1ba51974cd3e63c25015 Mon Sep 17 00:00:00 2001 From: Louis Poinsignon Date: Fri, 27 Dec 2019 13:31:11 -0800 Subject: [PATCH] Add XML CMS encoding/decoding --- ca/xml.go | 133 ++++++++++++++++++++++++++++++++++++++ validator/lib/cms.go | 27 +++++++- validator/lib/tal.go | 1 - validator/lib/xml.data | Bin 0 -> 1818 bytes validator/lib/xml.go | 88 +++++++++++++++++++++++++ validator/lib/xml_test.go | 71 ++++++++++++++++++++ 6 files changed, 317 insertions(+), 3 deletions(-) create mode 100644 ca/xml.go create mode 100644 validator/lib/xml.data create mode 100644 validator/lib/xml.go create mode 100644 validator/lib/xml_test.go diff --git a/ca/xml.go b/ca/xml.go new file mode 100644 index 0000000..259e23e --- /dev/null +++ b/ca/xml.go @@ -0,0 +1,133 @@ +package ca + +import ( + "bytes" + "encoding/xml" + "io" +) + +const ( + XML_VERSION_RFC8181 = 4 + XML_VERSION_RFC8183 = 1 +) + +type XMLMessage struct { + XMLName xml.Name `xml:"http://www.hactrn.net/uris/rpki/publication-spec/ msg"` + Version int `xml:"version,attr"` + Type string `xml:"type,attr"` + Inner string `xml:",innerxml"` +} + +type XMLMessageChildRequest struct { + XMLName xml.Name `xml:"http://www.hactrn.net/uris/rpki/rpki-setup/ child_request"` + Version int `xml:"version,attr"` + ChildHandle string `xml:"child_handle,attr"` + Tag string `xml:"tag,attr"` + Inner string `xml:",innerxml"` +} + +type XMLMessageParentResponse struct { + XMLName xml.Name `xml:"http://www.hactrn.net/uris/rpki/rpki-setup/ parent_response"` + Version int `xml:"version,attr"` + Tag string `xml:"tag,attr"` + ServiceURI string `xml:"service_uri,attr"` + ChildHandle string `xml:"child_handle,attr"` + ParentHandle string `xml:"parent_handle,attr"` + Inner string `xml:",innerxml"` +} + +type XMLMessagePublisherRequest struct { + XMLName xml.Name `xml:"http://www.hactrn.net/uris/rpki/rpki-setup/ publisher_request"` + Version int `xml:"version,attr"` + Tag string `xml:"tag,attr"` + PublisherHandle string `xml:"publisher_handle,attr"` + Inner string `xml:",innerxml"` +} + +type XMLMessageRepositoryResponse struct { + XMLName xml.Name `xml:"http://www.hactrn.net/uris/rpki/rpki-setup/ repository_response"` + Version int `xml:"version,attr"` + Tag string `xml:"tag,attr"` + ServiceURI string `xml:"service_uri,attr"` + SIABase string `xml:"sia_base,attr"` + RRDPNotificationURI string `xml:"rrdp_notification_uri,attr"` + PublisherHandle string `xml:"publisher_handle,attr"` + Inner string `xml:",innerxml"` +} + +func NewXMLList() *XMLMessage { + return &XMLMessage{ + Version: XML_VERSION_RFC8181, + Type: "query", + Inner: "", + } +} + +type Content struct { + XMLName xml.Name + Hash string `xml:"hash,attr"` + ErrorCode string `xml:"error_code,attr"` + Tag string `xml:"tag,attr"` + URI string `xml:"uri,attr"` + Inner string `xml:",innerxml"` +} + +func DecodeInner(inner []byte) ([]Content, error) { + var innerContent []Content + var err error + if len(inner) > 0 { + buf := bytes.NewBuffer(inner) + dec := xml.NewDecoder(buf) + for err == nil { + err = dec.Decode(&innerContent) + } + if err == io.EOF { + err = nil + } + } + return innerContent, err +} + +func DecodeXML(message []byte) (*XMLMessage, error) { + var msg XMLMessage + buf := bytes.NewBuffer(message) + dec := xml.NewDecoder(buf) + err := dec.Decode(&msg) + return &msg, err +} + +func DecodeXMLFull(message []byte) (*XMLMessage, []Content, error) { + var msg XMLMessage + buf := bytes.NewBuffer(message) + dec := xml.NewDecoder(buf) + err := dec.Decode(&msg) + if err != nil { + return nil, nil, err + } + innerContent, err := DecodeInner([]byte(msg.Inner)) + return &msg, innerContent, err +} + +func DecodeXMLCRFull(message []byte) (*XMLMessageChildRequest, []Content, error) { + var msg XMLMessageChildRequest + buf := bytes.NewBuffer(message) + dec := xml.NewDecoder(buf) + err := dec.Decode(&msg) + if err != nil { + return nil, nil, err + } + innerContent, err := DecodeInner([]byte(msg.Inner)) + return &msg, innerContent, err +} + +func DecodeXMLPRFull(message []byte) (*XMLMessageParentResponse, []Content, error) { + var msg XMLMessageParentResponse + buf := bytes.NewBuffer(message) + dec := xml.NewDecoder(buf) + err := dec.Decode(&msg) + if err != nil { + return nil, nil, err + } + innerContent, err := DecodeInner([]byte(msg.Inner)) + return &msg, innerContent, err +} diff --git a/validator/lib/cms.go b/validator/lib/cms.go index 732ba40..f6f4ba3 100644 --- a/validator/lib/cms.go +++ b/validator/lib/cms.go @@ -189,6 +189,10 @@ func EncryptSignatureRSA(rand io.Reader, signature []byte, privKey *rsa.PrivateK // Pass fullbytes of any EContent // Do one for ROA and MFT func EContentToEncap(econtent []byte) ([]byte, error) { + return EContentToEncapBF(econtent, false) +} + +func EContentToEncapBF(econtent []byte, skipbf bool) ([]byte, error) { var inner asn1.RawValue _, err := asn1.Unmarshal(econtent, &inner) if err != nil { @@ -199,7 +203,10 @@ func EContentToEncap(econtent []byte) ([]byte, error) { if err != nil { return inner2.Bytes, err } - fullbytes, _, err := BadFormatGroup(inner2.Bytes) + fullbytes := inner2.Bytes + if !skipbf { + fullbytes, _, err = BadFormatGroup(inner2.Bytes) + } return fullbytes, err } @@ -261,6 +268,15 @@ func (cms *CMS) Sign(rand io.Reader, ski []byte, encap []byte, priv interface{}, return nil } +func (cms *CMS) AddCRLs(crls []byte) error { + crlsM, err := asn1.MarshalWithParams([]asn1.RawValue{asn1.RawValue{FullBytes: crls}}, "tag:1,optional") + if err != nil { + return err + } + cms.SignedData.CRLs = asn1.RawValue{FullBytes: crlsM} + return nil +} + // Won't validate if signedattributes is empty func (cms *CMS) Validate(encap []byte, cert *x509.Certificate) error { signedAttributes := cms.SignedData.SignerInfos[0].SignedAttrs @@ -378,8 +394,15 @@ func EncodeCMS(certificate []byte, encapContent interface{}, signingTime time.Ti } val.FullBytes = mftBytes signOid = ManifestOID + case *XML: + xmlBytes, err := asn1.Marshal(*ec) + if err != nil { + return nil, err + } + val.FullBytes = xmlBytes + signOid = XMLOID default: - return nil, errors.New("Unknown type of content (not ROA or Manifest)") + return nil, errors.New("Unknown type of content (not ROA, Manifest or XML)") } certificateBytes, err := asn1.MarshalWithParams(certificate, "tag:0,implicit") diff --git a/validator/lib/tal.go b/validator/lib/tal.go index 1b4a5d6..16cfb38 100644 --- a/validator/lib/tal.go +++ b/validator/lib/tal.go @@ -86,7 +86,6 @@ func HashRSAPublicKey(key rsa.PublicKey) ([]byte, error) { if err != nil { return nil, err } - //fmt.Printf("TESTA A %x\n", keyBytesHash) hash := sha1.Sum(keyBytesHash) return hash[:], nil diff --git a/validator/lib/xml.data b/validator/lib/xml.data new file mode 100644 index 0000000000000000000000000000000000000000..e8d3cdddd02c52a82c2a9ed4fce0c9075acda6a6 GIT binary patch literal 1818 zcmb`HX;4#V6ozxNnm~*s7z09$iCbCB%|;SMmTcMi-?(a7IJix_}gx>C*tcs&&CMTt+ z)-dALYUL^xDBbiuo(38 zm^2j}u3_*Pbaj?8W(^}%6O)$3@FCFY-bo26HJ#-{@MgiE_!uA*3xrVaAcUkC;Ng%+ z004=v=LYOB2$0KWgDfoSBQ%P#!k^2P@wr?PCJv`OBEwyq zmqs|9yF5y-Qnq%SdYYx}5Wo9;=Z^W*KXnr)5^Z`{WrgQphw&ycZ?>#Gc7Ea66_KTc z=c)6?zcC7qS;kxmwX~og?_@8=FUTyp8eB6kV^>UFR@bXYOhx$sKRRJ3Cpq`!Mi zX6+#?)zS%##_<^^;sucLvV-}ug9cJ}I#&B^5RvRmxFg}2KQp6CTwnBi$lu;IdrgFiSjeATd2Zy%MN{T`w*?P+(*sjE4(=2%e8e;^`!DG?z06I3T|eu*_M4|r04M` zlvM$6f-Lk{>cti^c0XZh+gBj{^A&pqz>>HzJT?5wJd^5DS&r2(&ZcUb7rSp@-m z;d?6vHUE=W{^gW6A@8Me40AzyknmC}M>K5Ef4lRQB zk_Ux<@rauJ-4J3my<7XKf8p1?#0kdW*sdrQ*~dlOz~2!_Ih*SnxJ{2e$8^r~TNSzY zGnJ(LdZPc-{32SM18*#GSWxWSpB`_%b3KH*!%zP0$BX~i@0stRop1Q@1B!(<)x4D0 z+;Vo2Y^WERk%#2u)DyQ`L)giEM})ig%I+-d4t-}IgGw&;nh8^tS_=k-*5a>pMn9>^ zKe_0^56PMjL%2Xd!}G?l*rLx)ZP8(WQY_FCZq-$!ml>I)m;^Cm(^QczMHeCqZOzbo zf}>v_#A4%c?6ZR#^G&TwxRzp|tUpl{K5?wDD4h)f$6-m1!f@UB-?#oYa1Du|^J~K4 zVI2R1zo5&T8iq}JqXzdCwODVeXf^{)*Le#M$IZ4wX7>r);}$KgD*DP=zwNXonx^%b z15tsFZ#xL46&ivK6y9*dTwA%Prg`Ard};ObTed8+p{D{OKZb#|3rd&8vYIwm$W;j%+U7P4Bh_OC?VzH6@3h zeWR##M`111^PJKa9|P{To`8TL0=;_p}- zf+^G6UGn;Bf{!2CV#Q?r`D=T2N#()*AW6N}vz7#zbkf=HS7>(fm}wOw_|OI91;L>r zz=P#Pr%Ip literal 0 HcmV?d00001 diff --git a/validator/lib/xml.go b/validator/lib/xml.go new file mode 100644 index 0000000..26454d5 --- /dev/null +++ b/validator/lib/xml.go @@ -0,0 +1,88 @@ +package librpki + +import ( + "bytes" + "encoding/asn1" + "encoding/xml" +) + +var ( + XMLOID = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 1, 28} +) + +type XML struct { + OID asn1.ObjectIdentifier + EContent asn1.RawValue `asn1:"tag:0,explicit,optional"` +} + +type XMLContent struct { + Message interface{} +} + +type RPKI_XML struct { + Content []byte + Certificate *RPKI_Certificate + + InnerValid bool + InnerValidityError error +} + +func EncodeXMLContent(content interface{}) (*XML, error) { + buf := bytes.NewBuffer([]byte{}) + enc := xml.NewEncoder(buf) + err := enc.Encode(content) + if err != nil { + return nil, err + } + return EncodeXMLData(buf.Bytes()) +} + +func EncodeXMLData(message []byte) (*XML, error) { + eContentEnc, err := asn1.MarshalWithParams(message, "tag:0,explicit") + if err != nil { + return nil, err + } + + xmlContent := &XML{ + OID: XMLOID, + EContent: asn1.RawValue{FullBytes: eContentEnc}, + } + return xmlContent, nil +} + +func DecodeXML(data []byte) (*RPKI_XML, error) { + c, err := DecodeCMS(data) + if err != nil { + return nil, err + } + + var rawxml XML + _, err = asn1.Unmarshal(c.SignedData.EncapContentInfo.FullBytes, &rawxml) + if err != nil { + return nil, err + } + + var inner asn1.RawValue + _, err = asn1.Unmarshal(rawxml.EContent.Bytes, &inner) + if err != nil { + return nil, err + } + + var rpki_xml RPKI_XML + rpki_xml.Content = inner.Bytes + + cert, err := c.GetRPKICertificate() + if err != nil { + return &rpki_xml, err + } + rpki_xml.Certificate = cert + + err = c.Validate(inner.Bytes, cert.Certificate) + if err != nil { + rpki_xml.InnerValidityError = err + } else { + rpki_xml.InnerValid = true + } + + return &rpki_xml, nil +} diff --git a/validator/lib/xml_test.go b/validator/lib/xml_test.go new file mode 100644 index 0000000..cd50496 --- /dev/null +++ b/validator/lib/xml_test.go @@ -0,0 +1,71 @@ +package librpki + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/hex" + "github.com/stretchr/testify/assert" + "math/big" + "strings" + "testing" + "time" +) + +func TestEncodeXMLContent(t *testing.T) { + msg := []byte(``) + contentEnc, err := EncodeXMLData(msg) + assert.Nil(t, err) + + now := time.Now().UTC() + cms, err := EncodeCMS(nil, contentEnc, now) + assert.Nil(t, err) + + privkeyParent, err := rsa.GenerateKey(rand.Reader, 2048) + skiParent, _ := HashRSAPublicKey(*privkeyParent.Public().(*rsa.PublicKey)) + + parentCert := &x509.Certificate{ + Version: 1, + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: strings.ToUpper(hex.EncodeToString(skiParent)), + }, + NotBefore: now.Add(-time.Minute * 5), + NotAfter: now.Add(time.Hour * 24 * (365*100 + 24)), + SubjectKeyId: skiParent, + } + + privkey, err := rsa.GenerateKey(rand.Reader, 2048) + ski, _ := HashRSAPublicKey(*privkey.Public().(*rsa.PublicKey)) + + cert := &x509.Certificate{ + Version: 1, + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: strings.ToUpper(hex.EncodeToString(ski)), + }, + NotBefore: now.Add(-time.Minute * 5), + NotAfter: now.Add(time.Hour * 24 * (365*100 + 24)), + SubjectKeyId: ski, + } + pubkey := privkey.Public() + certBytes, err := x509.CreateCertificate(rand.Reader, cert, parentCert, pubkey, privkeyParent) + + crls, err := parentCert.CreateCRL(rand.Reader, privkeyParent, []pkix.RevokedCertificate{}, now.Add(-time.Minute*5), now.Add(time.Minute*5)) + assert.Nil(t, err) + cms.AddCRLs(crls) + + encap, _ := EContentToEncapBF(contentEnc.EContent.FullBytes, true) + err = cms.Sign(rand.Reader, ski, encap, privkey, certBytes) + assert.Nil(t, err) + + entriesBytes, err := asn1.Marshal(*cms) + assert.Nil(t, err) + + data, err := DecodeXML(entriesBytes) + assert.Nil(t, err) + assert.Equal(t, data.Content, msg) + assert.Equal(t, data.InnerValid, true) +}