I am attempting to get a repo (https://github.com/jchavannes/go-pgp) to encrypt.
When running the code I get
EOF # File: main.go Function: main.main Line: 21
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x52cdc2]
The consolidated code looks like:
package main
import (
"bytes"
"compress/gzip"
"crypto"
"errors"
"fmt"
"io"
"github.com/jimlawless/whereami"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/packet"
)
func main() {
// Create public key entity
publicKeyPacket, err := getPublicKeyPacket([]byte(TestPublicKey))
if err != nil {
fmt.Println(err.Error() + " # " + whereami.WhereAmI())
}
pubEntity, err := createEntityFromKeys(publicKeyPacket, nil)
if err != nil {
fmt.Println(err.Error() + " # " + whereami.WhereAmI())
}
// Encrypt message
encrypted, _ := encrypt(pubEntity, []byte(TestMessage))
fmt.Println(encrypted)
}
func getPublicKeyPacket(publicKey []byte) (*packet.PublicKey, error) {
publicKeyReader := bytes.NewReader(publicKey)
block, err := armor.Decode(publicKeyReader)
if err != nil {
return nil, err
}
if block.Type != openpgp.PublicKeyType {
return nil, errors.New("Invalid public key data")
}
packetReader := packet.NewReader(block.Body)
pkt, err := packetReader.Next()
if err != nil {
return nil, err
}
key, ok := pkt.(*packet.PublicKey)
if !ok {
return nil, err
}
return key, nil
}
func createEntityFromKeys(pubKey *packet.PublicKey, privKey *packet.PrivateKey) (*openpgp.Entity, error) {
config := packet.Config{
DefaultHash: crypto.SHA256,
DefaultCipher: packet.CipherAES256,
DefaultCompressionAlgo: packet.CompressionZLIB,
CompressionConfig: &packet.CompressionConfig{
Level: 9,
},
RSABits: 4096,
}
currentTime := config.Now()
uid := packet.NewUserId("", "", "")
e := openpgp.Entity{
PrimaryKey: pubKey,
PrivateKey: privKey,
Identities: make(map[string]*openpgp.Identity),
}
isPrimaryId := false
e.Identities[uid.Id] = &openpgp.Identity{
Name: uid.Name,
UserId: uid,
SelfSignature: &packet.Signature{
CreationTime: currentTime,
SigType: packet.SigTypePositiveCert,
PubKeyAlgo: packet.PubKeyAlgoRSA,
Hash: config.Hash(),
IsPrimaryId: &isPrimaryId,
FlagsValid: true,
FlagSign: true,
FlagCertify: true,
IssuerKeyId: &e.PrimaryKey.KeyId,
},
}
keyLifetimeSecs := uint32(86400 * 365)
e.Subkeys = make([]openpgp.Subkey, 1)
e.Subkeys[0] = openpgp.Subkey{
PublicKey: pubKey,
PrivateKey: privKey,
Sig: &packet.Signature{
CreationTime: currentTime,
SigType: packet.SigTypeSubkeyBinding,
PubKeyAlgo: packet.PubKeyAlgoRSA,
Hash: config.Hash(),
PreferredHash: []uint8{8}, // SHA-256
FlagsValid: true,
FlagEncryptStorage: true,
FlagEncryptCommunications: true,
IssuerKeyId: &e.PrimaryKey.KeyId,
KeyLifetimeSecs: &keyLifetimeSecs,
},
}
return &e, nil
}
func encrypt(entity *openpgp.Entity, message []byte) ([]byte, error) {
// Create buffer to write output to
buf := new(bytes.Buffer)
// Create encoder
encoderWriter, err := armor.Encode(buf, "Message", make(map[string]string))
if err != nil {
return []byte{}, fmt.Errorf("Error creating OpenPGP armor: %v", err)
}
// Create encryptor with encoder
encryptorWriter, err := openpgp.Encrypt(encoderWriter, []*openpgp.Entity{entity}, nil, nil, nil)
if err != nil {
return []byte{}, fmt.Errorf("Error creating entity for encryption: %v", err)
}
// Create compressor with encryptor
compressorWriter, err := gzip.NewWriterLevel(encryptorWriter, gzip.BestCompression)
if err != nil {
return []byte{}, fmt.Errorf("Invalid compression level: %v", err)
}
// Write message to compressor
messageReader := bytes.NewReader(message)
_, err = io.Copy(compressorWriter, messageReader)
if err != nil {
return []byte{}, fmt.Errorf("Error writing data to compressor: %v", err)
}
compressorWriter.Close()
encryptorWriter.Close()
encoderWriter.Close()
// Return buffer output - an encoded, encrypted, and compressed message
return buf.Bytes(), nil
}
const TestMessage = "hello world"
const TestPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
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
=5NT7
-----END PGP PRIVATE KEY BLOCK-----`
const TestPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
xsFNBFkNK+ABEADUpjJ/kz3j+iz9qnzUb6ONw+WHSLp8umnd1z06SBVkWFjYReqf
oPCOq67XDseK71ZSevrIt7EdTLAzl0xN8kB+8iedAGM5OCakDe3R8L83OGy1Em26
PbrrYs3TYKGDXW65TsGYCoETROGgU2zPvuBDU1RvVvd9vAlWHQis43BOWaaakCEc
00V3sdNcfV+lz7fNUXEgtmTCCr9NWX4gO3YeenIIxep4WD27VwscW5Q2B1cnxcFL
+TZzE2oVjtXljGSO94XsekuNU47zwJZNGyU6SSlSZ+KVXuSdkRRfNYHlgDWg5b8C
xVmdVUfsx3bmNlOlXoyETj83xvRlLxn3PYIgOz6OlYGba5oDogK2QLXGTXK1o9OE
kgoghmCNQqxocvb1hQXT8cEynIbAdc6/JknYaoic6ka1iTTz3uN8FEPw5gRlidcQ
3wkbmqIS0LJs3JmVbD7/BxMY1dwqMyvulfnLiTsWSPvk41o7dHf077t23V9w78Jg
h4Xq4HRvt37PtuO6eWW3c5aUIWmvvDqMbMEqp2y23noYoVNqEpVoHolDdoCSurv/
XxbNBnj46XwaIs6OlrO2htV0al2/WVTNnSLxCyoHXoJEDXyaOyNKn1jM/FczgYQJ
069uC804ohOfjLmbtUEYE7Hjeo5utPm2ryjnakgV5AStKgL0SyFZUwN/DwARAQAB
=gO1a
-----END PGP PUBLIC KEY BLOCK-----`
Any ideas how to fix this?
tl;dr
always check for errors.
The x/crypto/openpgp package is deprecated and you shouldn't be using it.
So here's your error:
EOF # File: main.go Function: main.main Line: 21
That's coming back from getPublicKeyPacket([]byte(TestPublicKey)), where we see:
publicKeyReader := bytes.NewReader(publicKey)
block, err := armor.Decode(publicKeyReader)
if err != nil {
return nil, err
}
Looking at the documentation for armor.Decode, we see (emphasis mine):
Decode reads a PGP armored block from the given Reader. It will ignore leading garbage. If it doesn't find a block, it will return nil, io.EOF.
So, it looks like you have an invalid public key block. Let's test that theory!
If we take the data from TestPublicKey and write it to a file...
-----BEGIN PGP PUBLIC KEY BLOCK-----
xsFNBFkNK+ABEADUpjJ/kz3j+iz9qnzUb6ONw+WHSLp8umnd1z06SBVkWFjYReqf
oPCOq67XDseK71ZSevrIt7EdTLAzl0xN8kB+8iedAGM5OCakDe3R8L83OGy1Em26
PbrrYs3TYKGDXW65TsGYCoETROGgU2zPvuBDU1RvVvd9vAlWHQis43BOWaaakCEc
00V3sdNcfV+lz7fNUXEgtmTCCr9NWX4gO3YeenIIxep4WD27VwscW5Q2B1cnxcFL
+TZzE2oVjtXljGSO94XsekuNU47zwJZNGyU6SSlSZ+KVXuSdkRRfNYHlgDWg5b8C
xVmdVUfsx3bmNlOlXoyETj83xvRlLxn3PYIgOz6OlYGba5oDogK2QLXGTXK1o9OE
kgoghmCNQqxocvb1hQXT8cEynIbAdc6/JknYaoic6ka1iTTz3uN8FEPw5gRlidcQ
3wkbmqIS0LJs3JmVbD7/BxMY1dwqMyvulfnLiTsWSPvk41o7dHf077t23V9w78Jg
h4Xq4HRvt37PtuO6eWW3c5aUIWmvvDqMbMEqp2y23noYoVNqEpVoHolDdoCSurv/
XxbNBnj46XwaIs6OlrO2htV0al2/WVTNnSLxCyoHXoJEDXyaOyNKn1jM/FczgYQJ
069uC804ohOfjLmbtUEYE7Hjeo5utPm2ryjnakgV5AStKgL0SyFZUwN/DwARAQAB
=gO1a
-----END PGP PUBLIC KEY BLOCK-----
...and then try to import that with gpg, we see:
$ gpg --import testpublickey.asc
gpg: invalid armor header: xsFNBFkNK+ABEADUpjJ/kz3j+iz9qnzUb6ONw+WHSLp8umnd1z06SBVkWFjYReqf\n
gpg: key 39078898F94F4667: new key but contains no user ID - skipped
It looks as if gpg agrees. Let's generate a proper public key and see if the problem goes away:
$ gpg --full-generate-key
[...]
pub rsa3072 2022-06-04 [SC]
92F9D24D5428E3D2B5FE5774A7F5A28D78317D17
uid Test Key <test#example.com>
sub rsa3072 2022-06-04 [E]
$ gpg --export -a test#example.com > testpublickey.asc
$ gpg --export-secret-key -a test#example.com > testsecretkey.asc
If we embed the content of testpublickey.asc into your code and compile, it now runs without errors...
$ go build
$ ./gpgtest
[]
...although it doesn't appear to be generating useful output. But that's because you're ignoring the error from encrypt. If we handle an error response:
encrypted, err := encrypt(pubEntity, []byte(TestMessage))
if err != nil {
panic(err)
}
fmt.Println(encrypted)
Then we see:
panic: Error creating entity for encryption: openpgp: invalid argument: cannot encrypt because no candidate hash functions are compiled in. (Wanted RIPEMD160 in this case.)
goroutine 1 [running]:
main.main()
/home/lars/tmp/go/main.go:31 +0xe9
And that error seems to be addressed here, which tells us the solution is to import the appropriate hash by adding:
import (
...
_ "golang.org/x/crypto/ripemd160"
)
With this change and a recompile, we now see as output:
[45 45 45 45 45 66 69 71 73 78 32 77 101 115 115 97 103 101 45 45 45 45 45 10 10 119 99 68 77 65 54 102 49 111 111 49 52 77 88 48 88 65 81 119 65 87 70 43 115 72 119 89 121 117 106 83 70 76 57 100 73 101 113 115 107 81 103 117 106 99 118 97 69 57 52 47 57 51 105 57 118 118 98 67 82 111 113 57 98 10 99 54 66 85 108 103 116 50 111 72 97 122 86 110 105 50 49 121 68 122 112 119 82 115 81 109 105 48 43 100 90 103 81 99 52 54 112 65 89 77 106 116 48 81 117 66 116 50 65 77 100 86 71 118 56 79 121 66 47 70 115 87 89 116 10 49 100 83 80 90 115 121 73 116 113 118 50 65 114 122 102 100 114 68 84 54 75 116 99 110 76 70 85 114 72 67 55 102 66 112 74 115 88 51 86 75 53 111 106 75 82 117 115 54 70 72 56 52 83 101 54 55 98 66 70 113 81 90 107 10 113 80 66 68 57 108 49 66 84 88 80 83 57 73 100 56 111 74 114 115 119 82 87 88 119 117 90 80 75 121 49 101 107 71 53 53 69 73 73 71 78 49 84 69 70 90 100 103 54 110 72 83 119 83 100 53 84 112 120 107 71 68 80 48 10 104 67 110 122 121 109 111 97 53 55 78 84 71 97 82 110 65 47 119 88 71 86 104 104 110 87 68 55 85 120 107 116 53 90 100 52 84 88 43 107 109 119 102 48 50 108 80 115 66 122 114 122 118 50 87 50 56 51 97 85 79 109 70 118 10 71 113 55 71 69 80 119 83 116 74 100 47 49 90 81 113 110 109 48 65 80 86 72 69 69 108 114 98 120 67 122 83 66 82 80 103 51 78 73 79 112 106 100 121 83 48 112 116 57 82 107 114 111 82 87 43 57 99 50 76 98 100 54 83 10 112 119 88 110 76 56 78 100 114 49 70 43 105 122 101 84 109 112 76 99 82 101 119 89 88 57 107 113 113 69 106 78 117 103 90 54 65 117 85 83 77 73 121 104 102 101 99 121 108 69 78 99 47 103 87 98 103 103 51 54 65 81 101 116 10 86 52 65 48 122 88 122 82 121 102 122 121 76 53 56 67 101 54 112 65 71 85 54 118 115 66 114 73 83 122 86 107 108 105 118 108 69 115 114 99 83 75 119 98 67 79 111 78 112 112 118 117 105 53 108 89 77 57 77 65 110 110 111 79 10 98 56 109 78 50 72 110 110 117 85 102 48 43 43 76 67 90 48 98 105 48 117 89 66 102 43 120 68 87 70 108 122 115 87 69 73 81 66 111 114 78 70 82 100 117 97 85 77 76 73 104 47 89 108 116 65 104 108 90 81 111 47 122 120 10 90 86 55 98 102 108 53 103 88 74 117 89 83 83 86 43 113 100 72 75 43 106 109 103 83 121 89 90 75 100 89 110 80 78 112 49 53 71 48 55 50 74 85 73 52 52 82 119 56 66 113 97 81 80 75 54 52 105 101 76 67 65 106 104 10 43 43 110 103 80 103 65 61 10 61 121 72 81 117 10 45 45 45 45 45 69 78 68 32 77 101 115 115 97 103 101 45 45 45 45 45]
Printing out a byte buffer isn't all that helpful, so let's wrap that with string:
fmt.Println(string(encrypted))
And that gives us:
-----BEGIN Message-----
wcDMA6f1oo14MX0XAQwABlLztwGC16in9i0kHm4M7FETC+1xgJDllBEGKqZWvPIb
FruPomy+jBPMROuVpZl9qxBTJwkTvhXxMZsSZz+mWOzrsAanhffZtCRbZ5BC+f8U
1iAkt35Sk73tN7w62+G7Unza0KlE8l3eqEb947wZSYAufdVnDESRd6ScTFuj/pxp
0pnfpaOAjjZPp9DOpExpU+yIij94t0UrAwndsSE3D3Bi1DfJ1Q9GPgYQbplwio3u
wfzFZOonIH2TG0AUwiY9lLVTu+u9rj5PovGdXuaIGTw26IAn05Xo3DRPdgee+W+2
qMHEDE6PoZFcL3eTJ8ed0IbvRAHZ3oovJpo/nJ2Pf7RfJcsujYYfsAv7Y2jmFWDd
cwo4NStRBCEZBhTfkZTJUI4kSpOtiXK2kqyQYuzKhQB3UnoMi4vbge9aOoT6x27f
u/UuvdRAuZ4CjD0ptyauFZKPwQxkYZ3R3LeUrPXbOQ1KPLWtnECseeqSqGVWpmVs
1DgjgF+cVXovuUFcWHhl0uYB/Q0NJCQV59t7QMtHalBIOv/KSOQUlOjGpRaZB5GL
NXRSSTLYZOUXqUpFMC8em+uoT3KFLxeYVG/5wB5YXyK/47CZvEvK32g54rS5dlHh
JergyAA=
=iYGx
-----END Message-----
But that same answer also tells us that the crypto/openpgp package is deprecated and you shouldn't be using it. The linked issue has some suggestions for alternatives.
Related
I am trying to encode a byte array as Base64 and running into two issues. I can do this with base64.StdEncoding.EncodedLen(text) but I'm worried that's costly, so I wanted to see if I could do it just with len(text). Here is the code (the functions are named "Marshal" because I'm using them as a field converter during JSON Marshaling):
package main
import (
"crypto/rand"
"encoding/base64"
"fmt"
)
func main() {
b := make([]byte, 60)
_, _ = rand.Read(b)
// Marshal Create Dst Buffer
MarshalTextBuffer(b)
// Marshal Convert to String
MarshalTextStringWithBufferLen(b)
// Marshal Convert to String
MarshalTextStringWithDecodedLen(b)
}
func MarshalTextBuffer(text []byte) error {
ba := base64.StdEncoding.EncodeToString(text)
fmt.Println(ba)
return nil
}
func MarshalTextStringWithBufferLen(text []byte) error {
ba := make([]byte, len(text)+30) // Why does len(text) not suffice? Temporarily using '30' for now, just so it doesn't overrun.
base64.StdEncoding.Encode(ba, text)
fmt.Println(ba)
return nil
}
func MarshalTextStringWithDecodedLen(text []byte) error {
ba := make([]byte, base64.StdEncoding.EncodedLen(len(text)))
base64.StdEncoding.Encode(ba, text)
fmt.Println(ba)
return nil
}
Here's the output:
IL5CW8T9WSgwU5Hyi9JsLLkU/EcydY6pG2fgLQJsMaXgxhSh74RTagzr6b9yDeZ8CP4Azc8xqq5/+Cgk
[73 76 53 67 87 56 84 57 87 83 103 119 85 53 72 121 105 57 74 115 76 76 107 85 47 69 99 121 100 89 54 112 71 50 102 103 76 81 74 115 77 97 88 103 120 104 83 104 55 52 82 84 97 103 122 114 54 98 57 121 68 101 90 56 67 80 52 65 122 99 56 120 113 113 53 47 43 67 103 107 0 0 0 0 0 0 0 0 0 0]
[73 76 53 67 87 56 84 57 87 83 103 119 85 53 72 121 105 57 74 115 76 76 107 85 47 69 99 121 100 89 54 112 71 50 102 103 76 81 74 115 77 97 88 103 120 104 83 104 55 52 82 84 97 103 122 114 54 98 57 121 68 101 90 56 67 80 52 65 122 99 56 120 113 113 53 47 43 67 103 107]
Why does the middle one MarshalTextStringWithBufferLen require extra padding?
Is base64.StdEncoding.EncodedLen a costly function (e.g. I can solve it with the bottom function, but I worry about the cost).
Base-64 encoding stores binary data (8 bits per byte) as text (using 6 bits per byte), so every 3 bytes is encoded as 4 bytes (3x8 = 4x6). So len(text) + 30 in your code is wrong, and should be len(text)*4/3 (if len(text) is divisible by 3) but to make for readability and to avoid bugs you should be using base64.StdEncoding.EncodedLen() to get the length.
If you look at the code for base64.StdEncoding.EncodedLen you will see that it is as fast as doing the calcs yourself (esp. as it will be in-lined).
I'm new to both Hasura/Graphql and GO. I've succeeded in writing a couple of Hasura actions which are supported by a GO server which in turn calls the Hasura server to run a query. These both work. For some reason the third one does not and I cannot see why. I don't get any errors from my GO server. It just doesn't return the same result as running the query directly in Hasura GraphiQL and I would be much appreciative of any help someone can provide.
I've defined an Action as follows -
type Query {
lookupCostCentreLocalDB (arg1: InputCostCentreAndId!): ValidCostCentreDtl
}
input InputCostCentreAndId {costCentre : String! altId : String!
}
type ValidCostCentreDtl {validCostCentre : String!
}
When I run the following query from the Hasura GraphiQL it works fine
query {GLW_GL_MAP (where: {_and: [{C3_Cost_Centre: {_eq: "8106"}},{idAlt: {_eq: 2}}]}){C3_Cost_Centre}}
and returns a cost centre
The action triggers my GO Server which builds the same query inserting the parameters and sends it to the Hasura URL
query lookingUpCCLocalDB_Test_True {
lookupCostCentreLocalDB (arg1: {costCentre: "8106" altId: "2"}) {ValidCostCentreDtl: validCostCentre}
}
but it returns a different result - in this case it does not return a cost centre. The call to Hasura is done as follows
//Prepare the HTTP Request
url := "http://localhost:8080/v1/graphql"
respRqst, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonQueryStr))
if err != nil {
fmt.Println("Error from http.NewRequest - err =", err)
panic(err)
}
fmt.Println("jsonQueryStr=", jsonQueryStr)
fmt.Println("respRqst=", respRqst)
respRqst.Header.Set("Content-Type", "application/json")
//Action the HTTP Request
client := http.Client{}
resp, err := client.Do(respRqst)
if err != nil {
fmt.Println("Error from client.Do - err =", err)
panic(err)
}
fmt.Println("resp=", resp)
//Close the HTTP Request when this function returns to ensure that it is always closed
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
bodyBytes, err = ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error ioutil.ReadAll(resp.Body)")
panic(err)
}
return bodyBytes, nil
}
The result of the Println statements is (the first item is a display of the []bytes variable passed to the http request and is the query in []bytes)
jsonQueryStr= [123 34 113 117 101 114 121 34 58 32 34 113 117 101 114 121 32 123 71 76 87 95 71 76 95 77
65 80 32 40 119 104 101 114 101 58 32 123 95 97 110 100 58 32 91 123 67 51 95 67 111 115 116 95 67 101
110 116 114 101 58 32 123 95 101 113 58 32 34 56 49 48 54 34 125 125 44 123 105 100 65 108 116 58 32 123 95
101 113 58 32 50 125 125 93 125 41 123 67 51 95 67 111 115 116 95 67 101 110 116 114 101 125 125 34 125]
respRqst= &{POST http://localhost:8080/v1/graphql HTTP/1.1 1 1 map[] {{"query": "query {GLW_GL_MAP (where: {_and: [{C3_Cost_Centre: {_eq: "8106"}},{idAlt: {_eq: "2"}}]}){C3_Cost_Centre}}"}} 0xb4b6c0 116 [] false localhost:8080 map[] map[] map[] 0xc0000120e0}
resp= &{200 OK 200 HTTP/1.1 1 1 map[Content-Type:[application/json; charset=utf-8] Date:[Wed, 04 Nov 2020 06:00:34 GMT] Server:[Warp/3.3.10]] 0xc000104340 -1 [chunked] false false map[] 0xc000124100 }
The following is a breakdown of the response
//Extract the data returned into the QueryResult data structure
var queryResult QueryLocalGLWCostCentre
err = json.Unmarshal(bodyBytes, &queryResult)
if err != nil {
fmt.Println("Unmarshal queryResult failed")
panic(err)
}
fmt.Println("bodyBytes =:", bodyBytes, "RESULT Length: ", len(queryResult.Data.GLWGLMAP), "queryResult=", queryResult, "queryResult.Data.GLWGLMAP=", queryResult.Data.GLWGLMAP)
The following is what was printed by the statements above
bodyBytes =: [123 34 101 114 114 111 114 115 34 58 91 123 34 101 120 116 101 110 115 105 111 110 115 34
58 123 34 112 97 116 104 34 58 34 36 34 44 34 99 111 100 101 34 58 34 105 110 118 97 108 105 100 45 106
115 111 110 34 125 44 34 109 101 115 115 97 103 101 34 58 34 69 114 114 111 114 32 105 110 32 36 58 32
70 97 105 108 101 100 32 114 101 97 100 105 110 103 58 32 115 97 116 105 115 102 121 46 32 69 120 112
101 99 116 105 110 103 32 39 44 39 32 111 114 32 39 125 39 32 97 116 32 39 56 49 48 54 125 125 44 123
105 100 65 108 116 58 123 95 101 113 58 50 125 125 93 125 41 123 67 51 95 67 111 115 116 95 67 101 110
116 114 101 125 125 125 39 34 125 93 125] RESULT Length: 0 queryResult= {{[]}}
queryResult.Data.GLWGLMAP= []
My GO struct for the response is as follows -
type QueryLookupGLWCostCentre struct {
Data struct {
GLWGLMAP []struct {
C3CostCentre string json:"C3_Cost_Centre"
} json:"GLW_GL_MAP"
} json:"data"
}
Can anyone provide any clues on what my problem is?
Regards
I managed to resolve my problem by using the following process to build the required Query for the Hasura Server
//Set up the query template to use
fullQueryOriginalTemplate := `{"query": "query C3MS_query {GLW_GL_MAP (where: {_and: [{C3_Cost_Centre: {_eq: \":1\"}},{id: {_eq: \":2\"}}]}){C3_Cost_Centre}}"}`
//Replace the parameters with the supplied values
fullQueryInterim := strings.Replace(fullQueryOriginalTemplate, ":1", costCentre, -1)
fullQuery := strings.Replace(fullQueryInterim, ":2", strconv.Itoa(id), -1)
I am using Bcrypt in Go to hash and compare the password given by the user. The thing is in the login, when I compare the password using CompareHashAndPassword it never matches so always says that the password is incorrect. Based on the concept of hash is supposed that with the same input we will have anytime the same output, and this is not my case.
**My code to hash (in the sign up) **
bs, err := bcrypt.GenerateFromPassword([]byte(Password), bcrypt.MinCost)
What I did
Send as password: 12345
When I print bs I get:
Attempt 1: [36 50 97 36 48 52 36 49 104 78 117 77 56 73 113 99 114 78 99 111 100 57 57 120 101 69 117 118 117 103 87 108 68 76 88 70 119 110 65 116 68 108 118
57 68 86 81 88 77 50 71 78 101 81 104 65 54 67 107 121]
Attempt 2:
[36 50 97 36 48 52 36 47 50 84 70 73 120 56 70 67 116 69 101 48 113 86 89 103 89 119 71 97 46 120 77 116 83 86 57 56 112 122 66 103 46 106 74 104 10
8 82 113 117 85 110 51 103 115 107 109 102 109 49 115 113]
Attempt 3:
[36 50 97 36 48 52 36 51 103 97 117 103 49 74 110 113 85 101 113 54 121 69 108 109 72 76 108 72 46 85 121 65 87 122 103 119 88 71 82 114 56 105 65 6
9 49 113 73 112 52 48 85 69 85 47 118 56 56 47 48 67]
Correct me if I am wrong, but in all that attempts the result should not be the same?
Then, I save that values in the database and these are the values for each attempt:
$2a$04$1hNuM8IqcrNcod99xeEuvugWlDLXFwnAtDlv9DVQXM2GNeQhA6Cky
$2a$04$/2TFIx8FCtEe0qVYgYwGa.xMtSV98pzBg.jJhlRquUn3gskmfm1sq
$2a$04$3gaug1JnqUeq6yElmHLlH.UyAWzgwXGRr8iAE1qIp40UEU/v88/0C
Then, to compare the password, in the login:
err := bcrypt.CompareHashAndPassword(user.Password, []byte(p))
user.Password is a []byte this value is conusulted from the database
Thank you
p is the password send in the form by the user
Bcrypt generates a random salt (that is included as a part of the resulting hash). So it is different every time with purpose.
You need to use bcrypt.CompareHashAndPassword to compare the hashed password and the plaintext password.
The first argument of bcrypt.CompareHashAndPassword is the hashed password, the second is the plaintext password. So you passed them in the wrong order.
WARNING: the cost you've chosen 4 is extremely low. Consider choosing something like 10 or over.
I am working on a API in Golang, am using Gorm as ORM. Currently am having an issue with a []byte field, I have it defined in my struct as:
type Member struct {
MyField []byte `gorm:"column:MyField" schema:"-"`
}
Then, I have the methods to Save and Read that resource, so the value when I save it is different to value that I am reading. For example,am using bcrypt to generate the hashed password, trying with 12345 the result is:
[36 50 97 36 49 48 36 46 56 98 88 72 82 71 113 66 100 65 105 103 70 119 114 97 73 77 99 78 117 106 54 78 103 88 68 49 56 110 103 112 105 86
104 79 117 47 114 57 116 51 47 53 97 100 109 103 106 46 68 109]
Until there everything is ok, then when I read the register from the database and print that value I get:
[36 50 97 36 48 52 36 56 49 67 66 121 118 90 47 47 104 49 83 120 50 108 112 71 73 51 67 88 46 97 52 74 54 66 84 73 106 105 110 122 98 69 90 51
78 113 67 66 49 103 50 56 116 47 57 120 78 103 109 54]
They are different, why?
I used gorm to create the tables, and the type of that column is defined in my postgres database as bytea
The code
To save:
bs, err := bcrypt.GenerateFromPassword([]byte(Pass), bcrypt.DefaultCost)
if err != nil {
fmt.Println("Hey error!")
}
user.MyField = bs
db.Create(&user)
To Read:
db.First(&user,id)
fmt.Println(user.MyField)
I suppose your problem is a wrong id at db.First(&user,id). You probably get the wrong row. I've tried to repoduce your error but couldn't. The following code works fine:
bs, err := bcrypt.GenerateFromPassword([]byte("12345"), bcrypt.DefaultCost)
expected := bs
db.Create(&Member{MyField: bs})
var member Member
db.First(&member)
actual := member.MyField
if !bytes.Equal(actual, expected) {
panic("fields are not equal")
}
I am a beginner in Golang. I wrote a function that will take variable args and pass it to another function that accepts variable args. I used "exec.Command()" for the second one. Here is my program
package main
import "fmt"
import "os/exec"
func execute(command string, parameters ...string) {
cmd := exec.Command(command, parameters...)
fmt.Println("Path =", cmd.Path, "Args =", cmd.Args, "Dir =", cmd.Dir)
out,_ := cmd.Output()
fmt.Println("Output =", out)
}
func main() {
execute("ls", "-l")
}
I expected it to return the list of files in the current directory. Instead I get a strange result
# go build command.go
#./command
Path = /bin/ls Args = [ls -l] Dir =
Output = [116 111 116 97 108 32 57 49 52 52 10 45 114 119 120 114 45 120 114 45 120 32 49 32 114 111 111 116 32 114 111 111 116 32 50 55 53 55 57 53 50 32 78 111 118 32 50 48 32 49 55 58 50 54 32 99 111 109 109 97 110 100 10 45 114 119 45 114 45 45 114 45 45 32 49 32 114 111 111 116 32 114 111 111 116 32 32 32 32 32 51 48 55 32 78 111 118 32 50 48 32 49 55 58 50 54 32 99 111 109 109 97 110 100 46 103 111 10 45 114 119 120 114 45 120 114 45 120 32 49 32 114 111 111 116 32 114 111 111 116 32 51 52 48 49 48 51 50 32 78 111 118 32 50 48 32 49 53 58 52 51 32 110 101 120 117 115 49 48 48 48 118 10 45 114 119 45 114 45 45 114 45 45 32 49 32 114 111 111 116 32 114 111 111 116 32 32 32 32 56 57 48 52 32 78 111 118 32 50 48 32 49 53 58 52 51 32 110 101 120 117 115 49 48 48 48 118 46 103 111 10 45 114 119 120 114 45 120 114 45 120 32 49 32 114 111 111 116 32 114 111 111 116 32 51 49 55 53 52 48 48 32 78 111 118 32 50 48 32 49 55 58 50 52 32 116 101 115 116 110 101 116 10 45 114 119 45 114 45 45 114 45 45 32 49 32 114 111 111 116 32 114 111 111 116 32 32 32 32 32 52 55 56 32 78 111 118 32 50 48 32 49 55 58 50 53 32 116 101 115 116 110 101 116 46 103 111 10]
What am I doing wrong
You are printing a byte slice.
fmt.Println("Output =", out)
Convert it to a string.
fmt.Println("Output =", string(out))
When using the fmt package, the formatting string is very concise:
fmt.Printf("%s\n", out)