mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-23 08:56:29 +00:00
AES to RSA-OAEP
This commit is contained in:
parent
016b032d0a
commit
f9ebf9ca11
7 changed files with 76 additions and 118 deletions
|
@ -183,7 +183,7 @@ func RunBuild(c *gin.Context) {
|
||||||
if repo.Params != nil && len(repo.Params) != 0 {
|
if repo.Params != nil && len(repo.Params) != 0 {
|
||||||
raw = []byte(inject.InjectSafe(string(raw), repo.Params))
|
raw = []byte(inject.InjectSafe(string(raw), repo.Params))
|
||||||
}
|
}
|
||||||
encrypted, _ := secure.Parse(repo.Hash, string(raw))
|
encrypted, _ := secure.Parse(repo, string(raw))
|
||||||
if encrypted != nil && len(encrypted) != 0 {
|
if encrypted != nil && len(encrypted) != 0 {
|
||||||
raw = []byte(inject.InjectSafe(string(raw), encrypted))
|
raw = []byte(inject.InjectSafe(string(raw), encrypted))
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,7 @@ func PostHook(c *gin.Context) {
|
||||||
if repo.Params != nil && len(repo.Params) != 0 {
|
if repo.Params != nil && len(repo.Params) != 0 {
|
||||||
raw = []byte(inject.InjectSafe(string(raw), repo.Params))
|
raw = []byte(inject.InjectSafe(string(raw), repo.Params))
|
||||||
}
|
}
|
||||||
encrypted, _ := secure.Parse(repo.Hash, string(raw))
|
encrypted, _ := secure.Parse(repo, string(raw))
|
||||||
if encrypted != nil && len(encrypted) != 0 {
|
if encrypted != nil && len(encrypted) != 0 {
|
||||||
raw = []byte(inject.InjectSafe(string(raw), encrypted))
|
raw = []byte(inject.InjectSafe(string(raw), encrypted))
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,7 +253,7 @@ func Encrypt(c *gin.Context) {
|
||||||
|
|
||||||
in := map[string]string{}
|
in := map[string]string{}
|
||||||
json.NewDecoder(c.Request.Body).Decode(&in)
|
json.NewDecoder(c.Request.Body).Decode(&in)
|
||||||
err := secure.EncryptMap(repo.Hash, in)
|
err := secure.EncryptMap(repo, in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fail(500, err)
|
c.Fail(500, err)
|
||||||
return
|
return
|
||||||
|
@ -261,7 +261,7 @@ func Encrypt(c *gin.Context) {
|
||||||
c.JSON(200, &in)
|
c.JSON(200, &in)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unubscribe accapets a request to unsubscribe the
|
// Unsubscribe accapets a request to unsubscribe the
|
||||||
// currently authenticated user to the repository.
|
// currently authenticated user to the repository.
|
||||||
//
|
//
|
||||||
// DEL /api/subscribers/:owner/:name
|
// DEL /api/subscribers/:owner/:name
|
||||||
|
|
|
@ -40,6 +40,18 @@ func MarshalPrivateKey(privkey *rsa.PrivateKey) []byte {
|
||||||
return privateKeyPEM
|
return privateKeyPEM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnMarshalPrivateKey is a helper function that unmarshals a PEM
|
||||||
|
// bytes to an RSA Private Key
|
||||||
|
func UnMarshalPrivateKey(privateKeyPEM []byte) *rsa.PrivateKey {
|
||||||
|
derBlock, _ := pem.Decode(privateKeyPEM)
|
||||||
|
privateKey, err := x509.ParsePKCS1PrivateKey(derBlock.Bytes)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return privateKey
|
||||||
|
}
|
||||||
|
|
||||||
// Encrypt is helper function to encrypt a plain-text string using
|
// Encrypt is helper function to encrypt a plain-text string using
|
||||||
// an RSA public key.
|
// an RSA public key.
|
||||||
func Encrypt(hash hash.Hash, pubkey *rsa.PublicKey, msg string) (string, error) {
|
func Encrypt(hash hash.Hash, pubkey *rsa.PublicKey, msg string) (string, error) {
|
||||||
|
|
|
@ -29,5 +29,12 @@ func TestSSHUtil(t *testing.T) {
|
||||||
g.Assert(err == nil).IsTrue()
|
g.Assert(err == nil).IsTrue()
|
||||||
g.Assert(decrypted == testMsg).IsTrue()
|
g.Assert(decrypted == testMsg).IsTrue()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
g.It("Unmarshals private key from PEM block", func() {
|
||||||
|
privateKeyPEM := MarshalPrivateKey(privkey)
|
||||||
|
privateKey := UnMarshalPrivateKey(privateKeyPEM)
|
||||||
|
|
||||||
|
g.Assert(privateKey.PublicKey.E == pubkey.E).IsTrue()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,79 +1,36 @@
|
||||||
package secure
|
package secure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/aes"
|
"crypto/sha256"
|
||||||
"crypto/cipher"
|
"hash"
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/drone/drone/Godeps/_workspace/src/gopkg.in/yaml.v2"
|
"github.com/drone/drone/Godeps/_workspace/src/gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
common "github.com/drone/drone/pkg/types"
|
||||||
|
"github.com/drone/drone/pkg/utils/sshutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse parses and returns the secure section of the
|
// Parse parses and returns the secure section of the
|
||||||
// yaml file as plaintext parameters.
|
// yaml file as plaintext parameters.
|
||||||
func Parse(key, raw string) (map[string]string, error) {
|
func Parse(repo *common.Repo, raw string) (map[string]string, error) {
|
||||||
params, err := parseSecure(raw)
|
params, err := parseSecure(raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = DecryptMap(key, params)
|
|
||||||
|
err = DecryptMap(repo, params)
|
||||||
return params, err
|
return params, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt encrypts a string to base64 crypto using AES.
|
// DecryptMap decrypts values of a map of named parameters
|
||||||
func Encrypt(key, text string) (_ string, err error) {
|
// from base64 to decrypted strings.
|
||||||
plaintext := []byte(text)
|
func DecryptMap(repo *common.Repo, params map[string]string) error {
|
||||||
|
|
||||||
block, err := aes.NewCipher(trimKey(key))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
|
|
||||||
iv := ciphertext[:aes.BlockSize]
|
|
||||||
if _, err = io.ReadFull(rand.Reader, iv); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
stream := cipher.NewCFBEncrypter(block, iv)
|
|
||||||
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
|
|
||||||
|
|
||||||
return base64.URLEncoding.EncodeToString(ciphertext), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt decrtyps from base64 to decrypted string.
|
|
||||||
func Decrypt(key, text string) (_ string, err error) {
|
|
||||||
ciphertext, err := base64.URLEncoding.DecodeString(text)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
block, err := aes.NewCipher(trimKey(key))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ciphertext) < aes.BlockSize {
|
|
||||||
err = fmt.Errorf("ciphertext too short")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iv := ciphertext[:aes.BlockSize]
|
|
||||||
ciphertext = ciphertext[aes.BlockSize:]
|
|
||||||
|
|
||||||
stream := cipher.NewCFBDecrypter(block, iv)
|
|
||||||
stream.XORKeyStream(ciphertext, ciphertext)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s", ciphertext), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecryptMap decrypts a map of named parameters
|
|
||||||
// from base64 to decrypted string.
|
|
||||||
func DecryptMap(key string, params map[string]string) error {
|
|
||||||
var err error
|
var err error
|
||||||
for name, value := range params {
|
hasher := toHash(repo.Hash)
|
||||||
params[name], err = Decrypt(key, value)
|
privKey := sshutil.UnMarshalPrivateKey([]byte(repo.Keys.Private))
|
||||||
|
|
||||||
|
for name, encrypted := range params {
|
||||||
|
params[name], err = sshutil.Decrypt(hasher, privKey, encrypted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -81,12 +38,14 @@ func DecryptMap(key string, params map[string]string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncryptMap encrypts encrypts a map of string parameters
|
// EncryptMap encrypts values of a map of named parameters
|
||||||
// to base64 crypto using AES.
|
func EncryptMap(repo *common.Repo, params map[string]string) error {
|
||||||
func EncryptMap(key string, params map[string]string) error {
|
|
||||||
var err error
|
var err error
|
||||||
|
hasher := toHash(repo.Hash)
|
||||||
|
privKey := sshutil.UnMarshalPrivateKey([]byte(repo.Keys.Private))
|
||||||
|
|
||||||
for name, value := range params {
|
for name, value := range params {
|
||||||
params[name], err = Encrypt(key, value)
|
params[name], err = sshutil.Encrypt(hasher, &privKey.PublicKey, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -94,22 +53,21 @@ func EncryptMap(key string, params map[string]string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper function that trims a key to a maximum
|
// parseSecure is helper function to parse the Secure data from
|
||||||
// of 32 bytes to match the expected AES block size.
|
|
||||||
func trimKey(key string) []byte {
|
|
||||||
b := []byte(key)
|
|
||||||
if len(b) > 32 {
|
|
||||||
b = b[:32]
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper function to parse the Secure data from
|
|
||||||
// the raw yaml file.
|
// the raw yaml file.
|
||||||
func parseSecure(raw string) (map[string]string, error) {
|
func parseSecure(raw string) (map[string]string, error) {
|
||||||
data := struct {
|
data := struct {
|
||||||
Secure map[string]string
|
Secure map[string]string
|
||||||
}{}
|
}{}
|
||||||
err := yaml.Unmarshal([]byte(raw), &data)
|
err := yaml.Unmarshal([]byte(raw), &data)
|
||||||
|
|
||||||
return data.Secure, err
|
return data.Secure, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// toHash is helper function to generate Hash of given string
|
||||||
|
func toHash(key string) hash.Hash {
|
||||||
|
hasher := sha256.New()
|
||||||
|
hasher.Write([]byte(key))
|
||||||
|
hasher.Reset()
|
||||||
|
return hasher
|
||||||
|
}
|
||||||
|
|
|
@ -4,58 +4,39 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
|
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
|
||||||
|
|
||||||
|
common "github.com/drone/drone/pkg/types"
|
||||||
|
"github.com/drone/drone/pkg/utils/sshutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Secure(t *testing.T) {
|
func Test_Secure(t *testing.T) {
|
||||||
|
|
||||||
g := goblin.Goblin(t)
|
g := goblin.Goblin(t)
|
||||||
g.Describe("Encrypt params", func() {
|
g.Describe("Encrypt params", func() {
|
||||||
|
privKey, _ := sshutil.GeneratePrivateKey()
|
||||||
key := "9T2tH3qZ8FSPr9uxrhzV4mn2VdVgA56xPVtYvCh0"
|
keypair := common.Keypair{
|
||||||
|
Private: string(sshutil.MarshalPrivateKey(privKey)),
|
||||||
|
Public: string(sshutil.MarshalPublicKey(&privKey.PublicKey)),
|
||||||
|
}
|
||||||
|
repo := common.Repo{
|
||||||
|
Hash: "9T2tH3qZ8FSPr9uxrhzV4mn2VdVgA56xPVtYvCh0",
|
||||||
|
Keys: &keypair,
|
||||||
|
}
|
||||||
|
hashKey := toHash(repo.Hash)
|
||||||
text := "super_duper_secret"
|
text := "super_duper_secret"
|
||||||
long := "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,32495A90F3FF199D\nlrMAsSjjkKiRxGdgR8p5kZJj0AFgdWYa3OT2snIXnN5+/p7j13PSkseUcrAFyokc\nV9pgeDfitAhb9lpdjxjjuxRcuQjBfmNVLPF9MFyNOvhrprGNukUh/12oSKO9dFEt\ns39F/2h6Ld5IQrGt3gZaBB1aGO+tw3ill1VBy2zGPIDeuSz6DS3GG/oQ2gLSSMP4\nOVfQ32Oajo496iHRkdIh/7Hho7BNzMYr1GxrYTcE9/Znr6xgeSdNT37CCeCH8cmP\naEAUgSMTeIMVSpILwkKeNvBURic1EWaqXRgPRIWK0vNyOCs/+jNoFISnV4pu1ROF\n92vayHDNSVw9wHcdSQ75XSE4Msawqv5U1iI7e2lD64uo1qhmJdrPcXDJQCiDbh+F\nhQhF+wAoLRvMNwwhg+LttL8vXqMDQl3olsWSvWPs6b/MZpB0qwd1bklzA6P+PeAU\nsfOvTqi9edIOfKqvXqTXEhBP8qC7ZtOKLGnryZb7W04SSVrNtuJUFRcLiqu+w/F/\nMSxGSGalYpzIZ1B5HLQqISgWMXdbt39uMeeooeZjkuI3VIllFjtybecjPR9ZYQPt\nFFEP1XqNXjLFmGh84TXtvGLWretWM1OZmN8UKKUeATqrr7zuh5AYGAIbXd8BvweL\nPigl9ei0hTculPqohvkoc5x1srPBvzHrirGlxOYjW3fc4kDgZpy+6ik5k5g7JWQD\nlbXCRz3HGazgUPeiwUr06a52vhgT7QuNIUZqdHb4IfCYs2pQTLHzQjAqvVk1mm2D\nkh4myIcTtf69BFcu/Wuptm3NaKd1nwk1squR6psvcTXOWII81pstnxNYkrokx4r2\n7YVllNruOD+cMDNZbIG2CwT6V9ukIS8tl9EJp8eyb0a1uAEc22BNOjYHPF50beWF\nukf3uc0SA+G3zhmXCM5sMf5OxVjKr5jgcir7kySY5KbmG71omYhczgr4H0qgxYo9\nZyj2wMKrTHLfFOpd4OOEun9Gi3srqlKZep7Hj7gNyUwZu1qiBvElmBVmp0HJxT0N\nmktuaVbaFgBsTS0/us1EqWvCA4REh1Ut/NoA9oG3JFt0lGDstTw1j+orDmIHOmSu\n7FKYzr0uCz14AkLMSOixdPD1F0YyED1NMVnRVXw77HiAFGmb0CDi2KEg70pEKpn3\nksa8oe0MQi6oEwlMsAxVTXOB1wblTBuSBeaECzTzWE+/DHF+QQfQi8kAjjSdmmMJ\nyN+shdBWHYRGYnxRkTatONhcDBIY7sZV7wolYHz/rf7dpYUZf37vdQnYV8FpO1um\nYa0GslyRJ5GqMBfDS1cQKne+FvVHxEE2YqEGBcOYhx/JI2soE8aA8W4XffN+DoEy\nZkinJ/+BOwJ/zUI9GZtwB4JXqbNEE+j7r7/fJO9KxfPp4MPK4YWu0H0EUWONpVwe\nTWtbRhQUCOe4PVSC/Vv1pstvMD/D+E/0L4GQNHxr+xyFxuvILty5lvFTxoAVYpqD\nu8gNhk3NWefTrlSkhY4N+tPP6o7E4t3y40nOA/d9qaqiid+lYcIDB0cJTpZvgeeQ\nijohxY3PHruU4vVZa37ITQnco9az6lsy18vbU0bOyK2fEZ2R9XVO8fH11jiV8oGH\n-----END RSA PRIVATE KEY-----"
|
encryptedValue, _ := sshutil.Encrypt(hashKey, &privKey.PublicKey, text)
|
||||||
|
|
||||||
g.It("Should encrypt a string", func() {
|
|
||||||
encrypted, err := Encrypt(key, text)
|
|
||||||
g.Assert(err == nil).IsTrue()
|
|
||||||
decrypted, err := Decrypt(key, encrypted)
|
|
||||||
g.Assert(err == nil).IsTrue()
|
|
||||||
g.Assert(text).Equal(decrypted)
|
|
||||||
})
|
|
||||||
|
|
||||||
g.It("Should encrypt a long string", func() {
|
|
||||||
encrypted, err := Encrypt(key, long)
|
|
||||||
g.Assert(err == nil).IsTrue()
|
|
||||||
decrypted, err := Decrypt(key, encrypted)
|
|
||||||
g.Assert(err == nil).IsTrue()
|
|
||||||
g.Assert(long).Equal(decrypted)
|
|
||||||
})
|
|
||||||
|
|
||||||
g.It("Should decrypt a map", func() {
|
|
||||||
params := map[string]string{
|
|
||||||
"foo": "2NQPoQfxPERVi42OEYzuVTjQrEQSrcN2-Pwk4kTlIVN5HA==",
|
|
||||||
}
|
|
||||||
err := DecryptMap(key, params)
|
|
||||||
g.Assert(err == nil).IsTrue()
|
|
||||||
g.Assert(params["foo"]).Equal("super_duper_secret")
|
|
||||||
})
|
|
||||||
|
|
||||||
g.It("Should trim a key with blocksize greater than 32 bytes", func() {
|
|
||||||
trimmed := trimKey("9T2tH3qZ8FSPr9uxrhzV4mn2VdVgA56x")
|
|
||||||
g.Assert(len(key) > 32).IsTrue()
|
|
||||||
g.Assert(len(trimmed)).Equal(32)
|
|
||||||
})
|
|
||||||
|
|
||||||
g.It("Should decrypt a yaml", func() {
|
g.It("Should decrypt a yaml", func() {
|
||||||
yaml := `secure: {"foo": "2NQPoQfxPERVi42OEYzuVTjQrEQSrcN2-Pwk4kTlIVN5HA=="}`
|
yaml := "secure: {\"foo\": \"" + encryptedValue + "\"}"
|
||||||
decrypted, err := Parse(key, yaml)
|
decrypted, err := Parse(&repo, yaml)
|
||||||
|
|
||||||
g.Assert(err == nil).IsTrue()
|
g.Assert(err == nil).IsTrue()
|
||||||
g.Assert(decrypted["foo"]).Equal("super_duper_secret")
|
g.Assert(decrypted["foo"]).Equal(text)
|
||||||
})
|
})
|
||||||
|
|
||||||
g.It("Should decrypt a yaml with no secure section", func() {
|
g.It("Should decrypt a yaml with no secure section", func() {
|
||||||
yaml := `foo: bar`
|
yaml := `foo: bar`
|
||||||
decrypted, err := Parse(key, yaml)
|
decrypted, err := Parse(&repo, yaml)
|
||||||
g.Assert(err == nil).IsTrue()
|
g.Assert(err == nil).IsTrue()
|
||||||
g.Assert(len(decrypted)).Equal(0)
|
g.Assert(len(decrypted)).Equal(0)
|
||||||
})
|
})
|
||||||
|
@ -64,10 +45,10 @@ func Test_Secure(t *testing.T) {
|
||||||
params := map[string]string{
|
params := map[string]string{
|
||||||
"foo": text,
|
"foo": text,
|
||||||
}
|
}
|
||||||
err := EncryptMap(key, params)
|
err := EncryptMap(&repo, params)
|
||||||
g.Assert(err == nil).IsTrue()
|
g.Assert(err == nil).IsTrue()
|
||||||
g.Assert(params["foo"] == "super_duper_secret").IsFalse()
|
g.Assert(params["foo"] == "super_duper_secret").IsFalse()
|
||||||
err = DecryptMap(key, params)
|
err = DecryptMap(&repo, params)
|
||||||
g.Assert(err == nil).IsTrue()
|
g.Assert(err == nil).IsTrue()
|
||||||
g.Assert(params["foo"] == "super_duper_secret").IsTrue()
|
g.Assert(params["foo"] == "super_duper_secret").IsTrue()
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue