How to use key pair generated by openpgp in go - go

I'm trying to generate keypair with openpgp lib and when I want to test it by encrypting a test string, it returns the following error openpgp: invalid argument: cannot encrypt because no candidate hash functions are compiled in. (Wanted RIPEMD160 in this case.). However it works when I pass a public key exported from gpg.
Also I'm wondering how to encrypt the private key like gpg --generate-key does?
func main() {
var e *openpgp.Entity
var pubKey *bytes.Buffer
e, _ = openpgp.NewEntity("testUser", "test", "test#test.test", nil)
for _, id := range e.Identities {
err := id.SelfSignature.SignUserId(id.UserId.Id, e.PrimaryKey, e.PrivateKey, nil)
if err != nil {
fmt.Println(err)
return
}
}
buf := new(bytes.Buffer)
w, err := armor.Encode(buf, openpgp.PublicKeyType, nil)
if err != nil {
fmt.Println(err)
return
}
e.Serialize(w)
w.Close()
pubKey = buf
// Encrypting test with public key
entity, err := openpgp.ReadArmoredKeyRing(pubKey)
if err != nil {
fmt.Println(err)
return
}
buf = new(bytes.Buffer)
encoderWriter, err := armor.Encode(buf, "PGP MESSAGE", make(map[string]string))
if err != nil {
fmt.Println(err)
return
}
encryptorWriter, err := openpgp.Encrypt(encoderWriter, entity, nil, nil, nil)
if err != nil {
fmt.Println(err)
return
}
encryptorWriter.Write([]byte("hello world"))
encryptorWriter.Close()
encoderWriter.Close()
fmt.Println(buf.String())
}

I had the exact same error.
TL; DR
It seems that it's an abandoned bug of the golang.org/x/crypto/openpgp package.
golang.org/x/crypto/openpgp package is frozen and deprecated. (= wontfix)
Use a patched fork package instead.
github.com/ProtonMail/go-crypto package # GitHub
TS; DR
Since the official "golang.org/x/crypto/openpgp" package was frozen and deprecated, as long as we use the "golang.org/x/crypto/openpgp" package, it seems that the only current workaround is to either;
Downgrade the Go version and the package, then blank import "_ golang.org/x/crypto/ripemd160" as #mh-cbon mentioned.
Patch the rejected PR on your own. (Rejected due to the freezing of x/crypto/openpgp package)
Patch: https://github.com/golang/crypto/pull/128/files
PR: Use correct default hashes and default ciphers when no preferences given
But I had to implement an OpenPGP key pair generator on Go 1.16.6. Don't ask why...
So, my current alternative was to use the forked package. Which was one of the abounding forks that the Go team mentioned as a sample.
github.com/ProtonMail/go-crypto package # GitHub
go get github.com/ProtonMail/go-crypto
Remove golang.org/x/crypto/openpgp from go.mod
Replace all the "golang.org/x/crypto/openpgp" to "github.com/ProtonMail/go-crypto/openpgp" in the source code.
go mod tidy
References
"x/crypto/openpgp: mark as frozen and deprecated" | Issue #44226 | go | golang # GitHub
"x/crypto/openpgp: new entities cannot be encrypted to by default" | Issue #37646 | go | golang # GitHub
"x/crypto/openpgp: new entities cannot be encrypted to by default" | Issue #12153 | go | golang # GitHub

Related

go-git checkout failing with reference not found

I'm just starting to use the go-git library and so far it looks very promising.
However, I'm trying to do a basic checkout of an existing branch and it is failing with "reference not found".
I have a simple repository, with several branches but one is "main" and another is "testoffmain".
Cloning, pulling and fetching the github repo work without issues.
Getting a hash for the branch seems to work fine as well:
repo, err := git.PlainOpen(localGitRepo)
w, err := repo.Worktree()
// Clone, Fetch, Pull all work, when I'm on the main branch
headRef, err := repo.Head()
newHashRef := plumbing.NewHashReference("refs/heads/testoffmain", headRef.Hash())
hashRef := plumbing.NewHash(newHashRef.String())
fmt.Printf("HashRef: %s", hashRef.String()) // Successfully displays the hash
// Returns a valid hash
revision := "origin/testoffmain"
revHash, err := repo.ResolveRevision(plumbing.Revision(revision))
fmt.Printf("HashRef: %s\n", revHash.String())
// Checkout fails with "reference not found"
referenceName := plumbing.ReferenceName("refs/heads/testoffmain")
err = w.Checkout(&git.CheckoutOptions{
Branch: referenceName,
Force: true})
if err != nil {
fmt.Printf("%s: Checkout: Cound not open local repository, Error: %s\n", project.LocalRepo, err)
return err
}
I've search around, and oddly can't find this simple usecase, so I'm assuming I'm doing something basic wrong.
I'm using github.com/go-git/go-git/v5 v5.5.2
I've tried a variety of options in
referenceName := plumbing.ReferenceName("refs/heads/testoffmain")
referenceName := plumbing.ReferenceName("testoffmain")
referenceName := plumbing.ReferenceName("origin/testoffmain")
referenceName := plumbing.ReferenceName("refs/origin/testoffmain")
referenceName := plumbing.ReferenceName("refs/remotes/origin/testoffmain")
referenceName := plumbing.ReferenceName("refs/remotes/testoffmain")
As I test, I tried copying .git/refs/remote/origin/testoffmain to .git/refs/head/testoffmain and I can the checkout to work, but then other issues occur.
You can list the referenceNames as below:
refs, _ := r.References()
refs.ForEach(func(ref *plumbing.Reference) error {
if ref.Type() == plumbing.HashReference {
fmt.Println(ref)
}
return nil
})
See official link.
And then we can use it for checkout:
w, err := r.Worktree()
if err != nil {
fmt.Println(err)
}
err = w.Checkout(&git.CheckoutOptions{
Branch: plumbing.ReferenceName("refs/remotes/origin/" + branchName),
})
if err != nil {
fmt.Println(err, branchName)
}
where branchName is the name of the git branch you want to switch

Why Do method of Conn interface is inlined

I created a redigo application with following code
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
_, err = conn.Do("HMSET", "album:1", "title", "Electric Ladyland", "artist", "Jimi Hendrix", "price", 4.95, "likes", 8)
if err != nil {
log.Fatal(err)
}
created a binary from above code and took the objdump of binary. I found that there is no definition of conn.Do in objdump as it became inline.
In redigo code, I changed the following function in conn.go but it did not work
//go:noinline
func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
return c.DoWithTimeout(c.readTimeout, cmd, args...)
}
Can someone suggest how make Do method noline?
As illustrated here, a //go:noinline pragma before a function declaration should have disabled inlining of that specific function.
But: if you are modifying github.com/gomodule/redigo/redis/conn.go, used in your project as a library (go get github.com/gomodule/redigo, added to your project go.mod/go.sum), that would not work.
Go would still compile the official cached module dependency for github.com/gomodule/redigo, not your local modification.
Hence, your //go:noinline is ignored.
You would need to fork the project, and go get that fork, and use a replace directive, as illustrated in "Go Module’s Replace" from nwillc:
replace github.com/pgavlin/femto => github.com/nwillc/femto v0.0.0-20201217031030-474e70183a86
That "replaced" the github.com/pgavlin/femto with my fork github.com/nwillc/femto.
And finally, back in snipgo I tried again:
go get -u github.com/nwillc/snipgo
Only then, any modification done to the source of (your forked) redigo repository would "stick" and be compiled.

How to take binary data file from an aes decryption and save as tar.gz archive

I started with the encrypted archive (foo.tar.gz.enc). I used Golang to decrypt the file successfully using aes-cbc-128, but the resulting data is a mess of binary that I decided to dump into foo.tar.gz.bin for now. Using go, how do I go about building the foo.tar.gz.bin file into the foo.tar.gz archive?
The data goes through functionally the same process as the OpenSSL call:
openssl enc -aes-128-cbc -d -iv xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -K xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -in Foo.tar.gz.enc -out Foo.tar.gz
...but without the ability to build the archive. If I save my resulting data to file (foo.tar.gz), it is unrecognizable as an archive.
This is the last chunk of my code that leaves me with the binary data:
key := hex.EncodeToString(decryptedKey)
f, err := ioutil.ReadFile(encryptedFilePath)
if err != nil {
log.Fatalf("Error reading encrypted tar.gz: %s", err)
}
cBlock, err := aes.NewCipher(key)
if err != nil {
log.Fatalf("New AES cipher error: %s", err)
}
if len(f) < aes.BlockSize {
log.Fatalf("cipher too short")
}
f = f[aes.BlockSize:]
if len(f)%aes.BlockSize != 0 {
log.Fatalf("cipher is not a multiple of the block size")
}
mode := cipher.NewCBCDecrypter(cBlock, iv)
mode.CryptBlocks(f, f)
I'd greatly appreciate it if anyone could shine a little light on the process. It's my last step in what has been a wonderful three-day-long decryption adventure.
Thanks in advance!
EDIT:
What Topaco points out in the comments below is absolutely correct. The IV is supplied to me. The line f = f[aes.BlockSize]: does not belong here and needs to be removed. I made some changes and handle RW a little differently. The code below results in a recognizable .tar.gz archive, albeit one that is 20 bytes smaller than the successfully decrypted archive, that throws a "truncated gzip data" error (not surprising when we're missing those 20 bytes).
Ignoring errors only in copy/paste for brevity.
f, err := os.Open(encryptedFilepath)
if err != nil {
log.Fatalf("Error reading encrypted tar.gz: %s", err)
}
defer f.Close()
cBlock, _ := aes.NewCipher([]byte(hexStrDecryptedData))
cbc := cipher.NewCBCDecrypter(cBlock, hexIV)
target := filepath.Join(sp.GetDirectory(), "Foo.tar.gz")
fout, _ := os.Create(target)
stats, _ := f.Stat()
var encBlock = make([]byte, 16)
var ReadProg int64 = 16
m := make([]byte, 16)
for ReadProg != stats.Size() {
if _, err := f.Read(encBlock); err != nil {
log.Fatal(err)
}
ReadProg += 16
mode.CryptBlocks(m, encBlock)
if ReadProg == stats.Size() {
pad := m[15]
m = m[:16-pad]
}
fout.Write(m)
}
EDIT 2:
Figured out that the whole issue was incorrect handling of the padding. Now that I've corrected it, all lights are a go. I will post the solution in the morning - time for sleep. Thanks for the help, Polk and Topaco.

deleting local branch in go-git: branch not found

I'm trying to delete a local branch using go-git, but getting an error branch not found when I run
branch := "refs/heads/template/test"
err = repo.DeleteBranch(branch)
or
err = repo.DeleteBranch(plumbing.ReferenceName(branch))
Using just the name of the branch (template/test) doesn't work either. The branch is included in the list of branches. This
refs, err := repo.Branches()
err = refs.ForEach(func(ref *plumbing.Reference) error {
fmt.Println(ref)
return nil
})
provides the following output:
f2d93bb67ced13936dbbbbfb44502abd42e7df13 refs/heads/global
df46ab083f17051afd6ca20e3ea4bfe01aedbb37 refs/heads/template/test
141f45305380aa0dc9f6802512ea76c5d48a87a1 refs/heads/template/test2
How can I delete it?
Update:
I checked the function DeleteBranch, it looks like this:
// DeleteBranch delete a Branch from the repository and delete the config
func (r *Repository) DeleteBranch(name string) error {
cfg, err := r.Storer.Config()
if err != nil {
return err
}
if _, ok := cfg.Branches[name]; !ok {
return ErrBranchNotFound
}
delete(cfg.Branches, name)
return r.Storer.SetConfig(cfg)
}
then I created cfg := repo.Storer.Config() and checked what cfg.Branches contains. Surprisingly, this map has only the following element: &{global origin refs/heads/global 0xc0007fbf80}. So other branches can't be deleted because they are not found in this config.
I know that this question is quite old but the naming here tripped me up as well so I thought I'd provide an answer.
Thanks to #Hephaestus and the post linked to, I was able to sort out the correct approach. If you're trying to delete a local branch (e.g. git branch -d foo) it looks like you're meant to use repo.Storer.RemoveReference.
In my case I already have a slice of (short) branch names:
for _, branchName := range branches {
ref := plumbing.NewBranchReferenceName(branchName)
err = repo.Storer.RemoveReference(ref)
if err != nil {
return "", err
}
}

Failing to verify ECDSA signature

I have a signature:
3044022014d647cd08f1ea5b31d1e6539b6cbceb9182f6e7b2e29fb969354ef7e3434923022028bb4eda36af410149baa936322e7c0e46cc5540a3aa89c811bc3c360028bfd301
a hash
f27c6c3aa42563c958292922be1e53fe107f4db0dfadba11122f0b12bf77f3ab
and a pubkey
04b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e6537a576782eba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c1e0908ef7b
Below is my Go code to verify the signature but it fails! I have information that the three pieces should validate OK and some other person has verified them in other ways. So I want to kown what mistake in this Go code make me get the false result.
if you want more information,this link is about how i build my signature and how someone say he success verify in other ways. I feel helpless now:
https://bitcointalk.org/index.php?topic=4879014.msg43992837#msg43992837
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"encoding/hex"
"fmt"
"math/big"
)
func main() {
pubkey, err := hex.DecodeString("b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e6537a576782eba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c1e0908ef7b")
if err != nil {
panic(err)
}
curve := elliptic.P256()
// length
keyLen := len(pubkey)
x := big.Int{}
y := big.Int{}
x.SetBytes(pubkey[:(keyLen / 2)])
y.SetBytes(pubkey[(keyLen / 2):])
rawPubKey := ecdsa.PublicKey{curve, &x, &y}
// hash
hash, err := hex.DecodeString("f27c6c3aa42563c958292922be1e53fe107f4db0dfadba11122f0b12bf77f3ab")
if err != nil {
panic(err)
}
r := big.Int{}
s := big.Int{}
rr, err := hex.DecodeString("14d647cd08f1ea5b31d1e6539b6cbceb9182f6e7b2e29fb969354ef7e3434923")
if err != nil {
panic(err)
}
ss, err := hex.DecodeString("28bb4eda36af410149baa936322e7c0e46cc5540a3aa89c811bc3c360028bfd3")
if err != nil {
panic(err)
}
r.SetBytes(rr)
s.SetBytes(ss)
fmt.Printf("%v\n", ecdsa.Verify(&rawPubKey, hash[:], &r, &s))
}
You are trying to use the wrong curve. P-256, also known as secp256r1, is not the same as the curve used in Bitcoin, which is secp256k1. Your public key doesn’t correspond to a point on P-256 (you can check with curve.IsOnCurve(&x, &y)), but it is a point on secp256k1.
Golang doesn’t include support for the Bitcoin curve, so you will need to find a library that does ECDSA using it.
I can confirm that signature does verify that data for that key if you use the correct curve.

Resources