feat(libcrypto): move into the Portainer repository EE-5476 (#10230)

pull/10231/head
andres-portainer 2023-09-01 17:27:19 -03:00 committed by GitHub
parent 9a234204fa
commit 090fa4aeb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 194 additions and 7 deletions

View File

@ -8,9 +8,9 @@ import (
"strings"
"time"
"github.com/portainer/libcrypto"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/internal/edge/cache"
"github.com/portainer/portainer/pkg/libcrypto"
"github.com/dchest/uniuri"
)

View File

@ -8,7 +8,7 @@ import (
"encoding/base64"
"encoding/hex"
"github.com/portainer/libcrypto"
"github.com/portainer/portainer/pkg/libcrypto"
)
const (

View File

@ -5,10 +5,10 @@ import (
"net/http"
"strings"
"github.com/portainer/libcrypto"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/client"
"github.com/portainer/portainer/pkg/libcrypto"
)
type motdResponse struct {

View File

@ -6,9 +6,9 @@ import (
"os"
"time"
"github.com/portainer/libcrypto"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/pkg/libcrypto"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"

1
go.mod
View File

@ -37,7 +37,6 @@ require (
github.com/orcaman/concurrent-map v1.0.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/portainer/libcrypto v0.0.0-20220506221303-1f4fb3b30f9a
github.com/portainer/libhttp v0.0.0-20230615144939-a999f666d9a9
github.com/robfig/cron/v3 v3.0.1
github.com/rs/zerolog v1.29.0

2
go.sum
View File

@ -311,8 +311,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/portainer/libcrypto v0.0.0-20220506221303-1f4fb3b30f9a h1:B0z3skIMT+OwVNJPQhKp52X+9OWW6A9n5UWig3lHBJk=
github.com/portainer/libcrypto v0.0.0-20220506221303-1f4fb3b30f9a/go.mod h1:n54EEIq+MM0NNtqLeCby8ljL+l275VpolXO0ibHegLE=
github.com/portainer/libhttp v0.0.0-20230615144939-a999f666d9a9 h1:Jq8g/pDcFL1Z/DnZgn6DyaWu29y9+RiB5aOJ/Xw4960=
github.com/portainer/libhttp v0.0.0-20230615144939-a999f666d9a9/go.mod h1:H49JLiywwLt2rrJVroafEWy8fIs0i7mThAThK40sbb8=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=

17
pkg/libcrypto/LICENSE Normal file
View File

@ -0,0 +1,17 @@
Copyright (c) 2018-2020 Portainer.io
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

3
pkg/libcrypto/README.md Normal file
View File

@ -0,0 +1,3 @@
# libcrypto
A small library providing encryption and decryption functions.

35
pkg/libcrypto/decrypt.go Normal file
View File

@ -0,0 +1,35 @@
package libcrypto
import (
"crypto/aes"
"crypto/cipher"
"errors"
)
// Decrypt decrypts data using 256-bit AES-GCM. This both hides the content of
// the data and provides a check that it hasn't been altered. Expects input
// form nonce|ciphertext|tag where '|' indicates concatenation.
// Creates a 32bit hash of the key before decrypting the data.
func Decrypt(data []byte, key []byte) ([]byte, error) {
hashKey := Hash32Bit(key)
block, err := aes.NewCipher(hashKey)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
if len(data) < gcm.NonceSize() {
return nil, errors.New("malformed ciphertext")
}
return gcm.Open(nil,
data[:gcm.NonceSize()],
data[gcm.NonceSize():],
nil,
)
}

34
pkg/libcrypto/encrypt.go Normal file
View File

@ -0,0 +1,34 @@
package libcrypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"io"
)
// Encrypt encrypts data using 256-bit AES-GCM. This both hides the content of
// the data and provides a check that it hasn't been altered. Output takes the
// form nonce|ciphertext|tag where '|' indicates concatenation.
// Creates a 32bit hash of the key before encrypting the data.
func Encrypt(data, key []byte) ([]byte, error) {
hashKey := Hash32Bit(key)
block, err := aes.NewCipher(hashKey)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return nil, err
}
return gcm.Seal(nonce, nonce, data, nil), nil
}

19
pkg/libcrypto/hash.go Normal file
View File

@ -0,0 +1,19 @@
package libcrypto
import (
"crypto/md5"
"encoding/hex"
)
// HashFromBytes returns the hash of the specified data
func HashFromBytes(data []byte) []byte {
digest := md5.New()
digest.Write(data)
return digest.Sum(nil)
}
// Hash32Bit returns a hexadecimal encoded hash
func Hash32Bit(data []byte) []byte {
hash := HashFromBytes(data)
return []byte(hex.EncodeToString(hash))
}

82
pkg/libcrypto/ssl.go Normal file
View File

@ -0,0 +1,82 @@
package libcrypto
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"errors"
"math/big"
"net"
"os"
"time"
)
// GenerateCertsForHost generates a self-signed certificate for host and saves them at certPath and keyPath
func GenerateCertsForHost(hostname, ip, certPath, keyPath string, expiry time.Time) error {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return err
}
template := x509.Certificate{
SerialNumber: serialNumber,
NotAfter: expiry,
NotBefore: time.Now(),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
parsedIP := net.ParseIP(ip)
if parsedIP == nil {
return errors.New("Failed parsing host ip")
}
template.DNSNames = append(template.DNSNames, hostname)
template.IPAddresses = append(template.IPAddresses, parsedIP)
keyPair, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return err
}
encodedCert, err := x509.CreateCertificate(rand.Reader, &template, &template, &keyPair.PublicKey, keyPair)
if err != nil {
return err
}
err = createPEMEncodedFile(certPath, "CERTIFICATE", encodedCert)
if err != nil {
return err
}
key, err := x509.MarshalECPrivateKey(keyPair)
if err != nil {
return err
}
err = createPEMEncodedFile(keyPath, "EC PRIVATE KEY", key)
if err != nil {
return err
}
return nil
}
func createPEMEncodedFile(path, header string, data []byte) error {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer file.Close()
err = pem.Encode(file, &pem.Block{Type: header, Bytes: data})
if err != nil {
return err
}
return nil
}