mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 08:22:24 +01:00
115 lines
3.7 KiB
Go
115 lines
3.7 KiB
Go
package xmlenc
|
|
|
|
import (
|
|
|
|
// nolint: gas
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/pem"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"github.com/beevik/etree"
|
|
)
|
|
|
|
// ErrAlgorithmNotImplemented is returned when encryption used is not
|
|
// supported.
|
|
type ErrAlgorithmNotImplemented string
|
|
|
|
func (e ErrAlgorithmNotImplemented) Error() string {
|
|
return "algorithm is not implemented: " + string(e)
|
|
}
|
|
|
|
// ErrCannotFindRequiredElement is returned by Decrypt when a required
|
|
// element cannot be found.
|
|
type ErrCannotFindRequiredElement string
|
|
|
|
func (e ErrCannotFindRequiredElement) Error() string {
|
|
return "cannot find required element: " + string(e)
|
|
}
|
|
|
|
// ErrIncorrectTag is returned when Decrypt is passed an element which
|
|
// is neither an EncryptedType nor an EncryptedKey
|
|
var ErrIncorrectTag = fmt.Errorf("tag must be an EncryptedType or EncryptedKey")
|
|
|
|
// ErrIncorrectKeyLength is returned when the fixed length key is not
|
|
// of the required length.
|
|
type ErrIncorrectKeyLength int
|
|
|
|
func (e ErrIncorrectKeyLength) Error() string {
|
|
return fmt.Sprintf("expected key to be %d bytes", int(e))
|
|
}
|
|
|
|
// ErrIncorrectKeyType is returned when the key is not the correct type
|
|
type ErrIncorrectKeyType string
|
|
|
|
func (e ErrIncorrectKeyType) Error() string {
|
|
return fmt.Sprintf("expected key to be %s", string(e))
|
|
}
|
|
|
|
// Decrypt decrypts the encrypted data using the provided key. If the
|
|
// data are encrypted using AES or 3DEC, then the key should be a []byte.
|
|
// If the data are encrypted with PKCS1v15 or RSA-OAEP-MGF1P then key should
|
|
// be a *rsa.PrivateKey.
|
|
func Decrypt(key interface{}, ciphertextEl *etree.Element) ([]byte, error) {
|
|
encryptionMethodEl := ciphertextEl.FindElement("./EncryptionMethod")
|
|
if encryptionMethodEl == nil {
|
|
return nil, ErrCannotFindRequiredElement("EncryptionMethod")
|
|
}
|
|
algorithm := encryptionMethodEl.SelectAttrValue("Algorithm", "")
|
|
decrypter, ok := decrypters[algorithm]
|
|
if !ok {
|
|
return nil, ErrAlgorithmNotImplemented(algorithm)
|
|
}
|
|
return decrypter.Decrypt(key, ciphertextEl)
|
|
}
|
|
|
|
func getCiphertext(encryptedKey *etree.Element) ([]byte, error) {
|
|
ciphertextEl := encryptedKey.FindElement("./CipherData/CipherValue")
|
|
if ciphertextEl == nil {
|
|
return nil, fmt.Errorf("cannot find CipherData element containing a CipherValue element")
|
|
}
|
|
ciphertext, err := base64.StdEncoding.DecodeString(strings.TrimSpace(ciphertextEl.Text()))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ciphertext, nil
|
|
}
|
|
|
|
func validateRSAKeyIfPresent(key interface{}, encryptedKey *etree.Element) (*rsa.PrivateKey, error) {
|
|
rsaKey, ok := key.(*rsa.PrivateKey)
|
|
if !ok {
|
|
return nil, errors.New("expected key to be a *rsa.PrivateKey")
|
|
}
|
|
|
|
// extract and verify that the public key matches the certificate
|
|
// this section is included to either let the service know up front
|
|
// if the key will work, or let the service provider know which key
|
|
// to use to decrypt the message. Either way, verification is not
|
|
// security-critical.
|
|
if el := encryptedKey.FindElement("./KeyInfo/X509Data/X509Certificate"); el != nil {
|
|
certPEMbuf := el.Text()
|
|
certPEMbuf = "-----BEGIN CERTIFICATE-----\n" + certPEMbuf + "\n-----END CERTIFICATE-----\n"
|
|
certPEM, _ := pem.Decode([]byte(certPEMbuf))
|
|
if certPEM == nil {
|
|
return nil, fmt.Errorf("invalid certificate")
|
|
}
|
|
cert, err := x509.ParseCertificate(certPEM.Bytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pubKey, ok := cert.PublicKey.(*rsa.PublicKey)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected certificate to be an *rsa.PublicKey")
|
|
}
|
|
if rsaKey.N.Cmp(pubKey.N) != 0 || rsaKey.E != pubKey.E {
|
|
return nil, fmt.Errorf("certificate does not match provided key")
|
|
}
|
|
} else if el = encryptedKey.FindElement("./KeyInfo/X509Data/X509IssuerSerial"); el != nil {
|
|
// TODO: determine how to validate the issuer serial information
|
|
}
|
|
return rsaKey, nil
|
|
}
|