Different hashes during CRAM-MD5 authentication - go

As an exercise, I'm trying to implement a mock SMTP server with CRAM-MD5 authentication in Go (without following RFC 2195, since it looks like it doesn't matter to the client what format the pre-hashed challenge is in; I also assume there is only one user "bob" with password "pass"). But I can't seem to get it right as the hash in response is always different from what I have on the server. I send the email using Go as such (running it as a separate package):
{...}
smtp.SendMail("localhost:25", smtp.CRAMMD5Auth("bob", "pass"),
"bob#localhost", []string{"alice#localhost"}, []byte("Hey Alice!\n"))
{...}
Here's what I do when I get the authentication acknowledgement from the client:
{...}
case strings.Contains(ms, "AUTH CRAM-MD5"):
rndbts = make([]byte, 16) // Declared at package level
b64b := make([]byte, base64.StdEncoding.EncodedLen(16))
rand.Read(rndbts)
base64.StdEncoding.Encode(b64b, rndbts)
_, err = conn.Write([]byte(fmt.Sprintf("334 %x\n", b64b)))
{...}
And this is what I do with the client's response:
{...}
{
ms = strings.TrimRight(ms, "\r\n") // The response to the challenge
ds, _ := base64.StdEncoding.DecodeString(ms)
s := strings.Split(string(ds), " ")
login := s[0] // I can get the login from the response.
h := hmac.New(md5.New, []byte("pass"))
h.Write(rndbts)
c := make([]byte, 0, ourHash.Size()) // From smtp/auth.go, not sure why we need this.
validPass := hmac.Equal(h.Sum(c), []byte(s[1]))
{...}
}
{...}
And the validPass is never true. I omitted error handling from the excerpts for brevity, but they're there in the actual code (though they're always nil). Why are the hashes different? I have looked at the source code for net/smtp, and it seems to me that I'm going in the right direction, but not quite.

I hope this helps! Runnable version is at https://play.golang.org/p/-8shx_IcLV. Also note that you'll need to fix your %x (should be %s) so you're sending the right challenge down to the client. Right now I think you're trying to hex-encode your base64 string.
Once you've fixed that, I believe this code should help you to construct the right response string on the server and compare it to what the client sent.
// Example values taken from http://susam.in/blog/auth-cram-md5/
challenge := []byte("<17893.1320679123#tesseract.susam.in>")
username := []byte("alice")
password := []byte("wonderland")
clientResponse := []byte("YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA=")
// hash the challenge with the user's password
h := hmac.New(md5.New, password)
h.Write(challenge)
hash := h.Sum(nil)
// encode the result in lowercase hexadecimal
hexEncoded := hex.EncodeToString(hash)
// prepend the username and a space
toEncode := []byte(string(username) + " " + hexEncoded)
// base64-encode the whole thing
b64Result := make([]byte, base64.StdEncoding.EncodedLen(len(toEncode)))
base64.StdEncoding.Encode(b64Result, toEncode)
// check that this is equal to what the client sent
if hmac.Equal(b64Result, clientResponse) {
fmt.Println("Matches!")
}

Related

When create JWT token inside loop getting same token in jwt-go

I am creating jwt tokens using jwt-go library. Later wrote a script to load test. I have noticed when I send the many concurrent request getting same token. To check more about this I created token inside for loop and result is same.
The library that I use is https://github.com/dgrijalva/jwt-go, go version is 1.12.9.
expirationTime := time.Now().Add(time.Duration(60) * time.Minute)
for i := 1; i < 5; i++ {
claims := &jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
Issuer:"telescope",
}
_token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
var jwtKey = []byte("secret_key")
auth_token, _ := _token.SignedString(jwtKey)
fmt.Println(auth_token)
}
A JWT contains three parts: a mostly-fixed header, a set of claims, and a signature. RFC 7519 has the actual details. If the header is fixed and the claims are identical between two tokens, then the signature will be identical too, and you can easily get duplicated tokens. The two timestamp claims "iat" and "exp" are only at a second granularity, so if you issue multiple tokens with your code during the same second you will get identical results (even if you move the expirationTime calculation inside the loop).
The jwt-go library exports the StandardClaims listed in RFC 7519 ยง4.1 as a structure, which is what you're using in your code. Digging through the library code, there's nothing especially subtle here: StandardClaims uses ordinary "encoding/json" annotations, and then when a token is written out, the claims are JSON encoded and then base64-encoded. So given a fixed input, you'll get a fixed output.
If you want every token to be "different" in some way, the standard "jti" claim is a place to provide a unique ID. This isn't part of the StandardClaims, so you need to create your own custom claim type that includes it.
type UniqueClaims struct {
jwt.StandardClaims
TokenId string `json:"jti,omitempty"`
}
Then when you create the claims structure, you need to generate a unique TokenId yourself.
import (
"crypto/rand"
"encoding/base64"
)
bits := make([]byte, 12)
_, err := rand.Read(bits)
if err != nil {
panic(err)
}
claims := UniqueClaims{
StandardClaims: jwt.StandardClaims{...},
TokenId: base64.StdEncoding.EncodeToString(bits),
}
https://play.golang.org/p/zDnkamwsCi- has a complete example; every time you run it you will get a different token, even if you run it multiple times in the same second. You can base64 decode the middle part of the token by hand to see the claims, or use a tool like the https://jwt.io/ debugger to decode it.
I changed your code:
Moved calculation of expirationTime in the loop
Added 1 sec delay on each step of loop
for i := 1; i < 5; i++ {
expirationTime := time.Now().Add(time.Duration(60) * time.Minute)
claims := &jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
Issuer: "telescope",
}
_token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
var jwtKey = []byte("secret_key")
auth_token, _ := _token.SignedString(jwtKey)
fmt.Println(auth_token)
time.Sleep(time.Duration(1) * time.Second)
}
In this case we get different tokens:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNDgsImlzcyI6InRlbGVzY29wZSJ9.G7wV-zsCYjysLEdgYAq_92JGDPsgqqOz9lZxdh5gcX8
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNDksImlzcyI6InRlbGVzY29wZSJ9.yPNV20EN3XJbGiHhe-wGTdiluJyVHXj3nIqEsfwDZ0Q
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNTAsImlzcyI6InRlbGVzY29wZSJ9.W3xFXEiVwh8xK47dZinpXFpKuvUl1LFUAiaLZZzZ2L0
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNTEsImlzcyI6InRlbGVzY29wZSJ9.wYUbzdXm_VQGdFH9RynAVVouW9h6KI1tHRFJ0Y322i4
Sorry, I am not big expert in JWT and I hope somebody who is explain us this behavior from RFC point of view.
I want to get different tokens. eg : same person login in to system using different browser. so I want to keep many tokens.
It is the same user and we can get him the same token. If we want to give it another one we need to revoke previous one or the client must refresh it.

Custom Transaction Processor not receiving request

Why is my transaction processor not receiving the request I post via the rest API?
I have built a client and Transaction Processor (TP), in Golang, which is not much different to the XO example. I have successfully got the TP running locally to the Sawtooth components, and sending batch lists from a separate cli tool. Currently the apply method in the TP is not being hit and does not receive any of my transactions.
EDIT: In order to simplify and clarify my problem as much as possible, I have abandoned my original source code, and built a simpler client that sends a transaction for the XO sdk example.*
When I run the tool that I have built, the rest api successfully receives the request, processes and returns a 202 response but appears to omit the id of the batch from the batch statuses url. Inspecting the logs it appears as though the validator never receives the request from the rest api, as illustrated in the logs below.
sawtooth-rest-api-default | [2018-05-16 09:16:38.861 DEBUG route_handlers] Sending CLIENT_BATCH_SUBMIT_REQUEST request to validator
sawtooth-rest-api-default | [2018-05-16 09:16:38.863 DEBUG route_handlers] Received CLIENT_BATCH_SUBMIT_RESPONSE response from validator with status OK
sawtooth-rest-api-default | [2018-05-16 09:16:38.863 INFO helpers] POST /batches HTTP/1.1: 202 status, 213 size, in 0.002275 s
My entire command line tool that sends transactions to a local instance is below.
package main
import (
"bytes"
"crypto/sha512"
"encoding/base64"
"encoding/hex"
"flag"
"fmt"
"io/ioutil"
"log"
"math/rand"
"net/http"
"strings"
"time"
"github.com/hyperledger/sawtooth-sdk-go/protobuf/batch_pb2"
"github.com/hyperledger/sawtooth-sdk-go/protobuf/transaction_pb2"
"github.com/hyperledger/sawtooth-sdk-go/signing"
)
var restAPI string
func main() {
var hostname, port string
flag.StringVar(&hostname, "hostname", "localhost", "The hostname to host the application on (default: localhost).")
flag.StringVar(&port, "port", "8080", "The port to listen on for connection (default: 8080)")
flag.StringVar(&restAPI, "restAPI", "http://localhost:8008", "The address of the sawtooth REST API")
flag.Parse()
s := time.Now()
ctx := signing.CreateContext("secp256k1")
key := ctx.NewRandomPrivateKey()
snr := signing.NewCryptoFactory(ctx).NewSigner(key)
payload := "testing_new,create,"
encoded := base64.StdEncoding.EncodeToString([]byte(payload))
trn := BuildTransaction(
"testing_new",
encoded,
"xo",
"1.0",
snr)
trn.Payload = []byte(encoded)
batchList := &batch_pb2.BatchList{
Batches: []*batch_pb2.Batch{
BuildBatch(
[]*transaction_pb2.Transaction{trn},
snr),
},
}
serialised := batchList.String()
fmt.Println(serialised)
resp, err := http.Post(
restAPI+"/batches",
"application/octet-stream",
bytes.NewReader([]byte(serialised)),
)
if err != nil {
fmt.Println("Error")
fmt.Println(err.Error())
return
}
defer resp.Body.Close()
fmt.Println(resp.Status)
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
elapsed := time.Since(s)
log.Printf("Creation took %s", elapsed)
resp.Close = true
}
// BuildTransaction will build a transaction based on the information provided
func BuildTransaction(ID, payload, familyName, familyVersion string, snr *signing.Signer) *transaction_pb2.Transaction {
publicKeyHex := snr.GetPublicKey().AsHex()
payloadHash := Hexdigest(string(payload))
addr := Hexdigest(familyName)[:6] + Hexdigest(ID)[:64]
transactionHeader := &transaction_pb2.TransactionHeader{
FamilyName: familyName,
FamilyVersion: familyVersion,
SignerPublicKey: publicKeyHex,
BatcherPublicKey: publicKeyHex,
Inputs: []string{addr},
Outputs: []string{addr},
Dependencies: []string{},
PayloadSha512: payloadHash,
Nonce: GenerateNonce(),
}
header := transactionHeader.String()
headerBytes := []byte(header)
headerSig := hex.EncodeToString(snr.Sign(headerBytes))
return &transaction_pb2.Transaction{
Header: headerBytes,
HeaderSignature: headerSig[:64],
Payload: []byte(payload),
}
}
// BuildBatch will build a batch using the provided transactions
func BuildBatch(trans []*transaction_pb2.Transaction, snr *signing.Signer) *batch_pb2.Batch {
ids := []string{}
for _, t := range trans {
ids = append(ids, t.HeaderSignature)
}
batchHeader := &batch_pb2.BatchHeader{
SignerPublicKey: snr.GetPublicKey().AsHex(),
TransactionIds: ids,
}
return &batch_pb2.Batch{
Header: []byte(batchHeader.String()),
HeaderSignature: hex.EncodeToString(snr.Sign([]byte(batchHeader.String())))[:64],
Transactions: trans,
}
}
// Hexdigest will hash the string and return the result as hex
func Hexdigest(str string) string {
hash := sha512.New()
hash.Write([]byte(str))
hashBytes := hash.Sum(nil)
return strings.ToLower(hex.EncodeToString(hashBytes))
}
// GenerateNonce will generate a random string to use
func GenerateNonce() string {
return randStringBytesMaskImprSrc(16)
}
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
func randStringBytesMaskImprSrc(n int) string {
rand.Seed(time.Now().UnixNano())
b := make([]byte, n)
// A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = rand.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}
There were a number of issues with this, and hopefully I can explain each in isolation to help shed a light on the ways in which these transactions can fail.
Transaction Completeness
As #Frank C. comments above my transaction headers were missing a couple of values. These were addresses and also the nonce.
// Hexdigest will hash the string and return the result as hex
func Hexdigest(str string) string {
hash := sha512.New()
hash.Write([]byte(str))
hashBytes := hash.Sum(nil)
return strings.ToLower(hex.EncodeToString(hashBytes))
}
addr := Hexdigest(familyName)[:6] + Hexdigest(ID)[:64]
transactionHeader := &transaction_pb2.TransactionHeader{
FamilyName: familyName,
FamilyVersion: familyVersion,
SignerPublicKey: publicKeyHex,
BatcherPublicKey: publicKeyHex,
Inputs: []string{addr},
Outputs: []string{addr},
Dependencies: []string{},
PayloadSha512: payloadHash,
Nonce: uuid.NewV4(),
}
Tracing
The next thing was to enable tracing in the batch.
return &batch_pb2.Batch{
Header: []byte(batchHeader.String()),
HeaderSignature: batchHeaderSignature,
Transactions: trans,
Trace: true, // Set this flag to true
}
With the above set the Rest API will decode the message to print additional logging information, and also the Validator component will output more useful logging.
400 Bad Request
{
"error": {
"code": 35,
"message": "The protobuf BatchList you submitted was malformed and could not be read.",
"title": "Protobuf Not Decodable"
}
}
The above was output by the Rest API once the tracing was turned on. This proved that there was something awry with the received data.
Why is this the case?
Following some valuable advice from the Sawtooth chat rooms, I tried to deserisalise my batches using the SDK for another language.
Deserialising
To test deserialising the batches in another SDK, I built a web api in python that I could easily send my batches to, which could attempt to deserialise them.
from flask import Flask, request
from protobuf import batch_pb2
app = Flask(__name__)
#app.route("/batches", methods = [ 'POST' ])
def deserialise():
received = request.data
print(received)
print("\n")
print(''.join('{:02x}'.format(x) for x in received))
batchlist = batch_pb2.BatchList()
batchlist.ParseFromString(received)
return ""
if __name__ == '__main__':
app.run(host="0.0.0.0", debug=True)
After sending my batch to this I received the below error.
RuntimeWarning: Unexpected end-group tag: Not all data was converted
This was obviously what was going wrong with my batches, but seeing as this was all being handled by the Hyperledger Sawtooth Go SDK, I decided to move to Python and built my application with that.
[Edit]
Managed to get document updated with information on writing a client in Go https://sawtooth.hyperledger.org/docs/core/nightly/master/app_developers_guide/go_sdk.html
[Original Answer]
Answering to the question a bit late, hope this will help others who are facing similar issues with Go client.
I was recently trying out a sample Go client for Sawtooth. Faced similar issues as you have asked here, currently it's hard to debug what has gone wrong in the composed batch list. Problem is lack of sample code and documentation for using Go SDK in client application development.
Here's a link for the sample Go code that's working: https://github.com/arsulegai/contentprotection/tree/master/ContentProtectionGoClient
Please have a look at the file src/client/client.go which composes single Transaction and Batch, puts it to batch list and sends it to the validator. Method I followed for debugging is to compose the expected batch list (for specific transaction) in another language and comparing each step with the result from equivalent step in Go code.
Coming to the question, along with missing information in the composed transaction header other problem could be the way protobuf messages are serialized. Please use protobuf library for serializing.
Example: (extending the answer from #danielcooperxyz)
transactionHeader := &transaction_pb2.TransactionHeader{
FamilyName: familyName,
FamilyVersion: familyVersion,
SignerPublicKey: publicKeyHex,
BatcherPublicKey: publicKeyHex,
Inputs: []string{addr},
Outputs: []string{addr},
Dependencies: []string{},
PayloadSha512: payloadHash,
Nonce: uuid.NewV4(),
}
transactionHeaderSerializedForm, _ := proto.Marshal(transactionHeader)
(protobuf library methods can be found in github.com/golang/protobuf/proto)

Golang DumpResponse gzip issue when logging response body (reverseproxy)

I have created a simple reverse proxy in my main.go as follows:
reverseproxy := httputil.NewSingleHostReverseProxy("https://someurl/someuri")
this is working fine, however, I would like to log the response that comes back from the server. I can also do this utilizing the following code inside the standard RoundTrip method recommended by golang:
response, err := http.DefaultTransport.RoundTrip(request)
dumpresp, err := httputil.DumpResponse(response, true)
if err != nil {
return nil, err
}
log.Printf("%s", dumpresp)
All the above works as expected aside from one thing, the response, when Content-Encoding: gzip, the string appears to the logs as non-utf8 gzip characters. it seems to be a golang oversight but maybe there is something i have missed in the documentation, which I have read to its completion a few times. I can't post the logs here because the characters are non utf8 so they would not display on this site anyway. So, I know what your thinking, just grab the gzip content and use a method to remove the gzip compression. That would be great if the response from DumpResponse was not partly gzip and partly standard utf8 with no way to separate the sections from each other.
So I know what your going to say, why not just take the raw response like the following and gzip decode, and "not" use DumpResponse. Well I can do that as well, but there is an issue with the following:
var reader io.Reader
startreader := httputility.NewChunkedReader(reader)
switch response.Header.Get("Content-Encoding") {
case "gzip":
startreader, err = gzip.NewReader(response.Body)
log.Println("Response body gzip: ")
buf := new(bytes.Buffer)
buf.ReadFrom(startreader)
b := buf.Bytes()
s := *(*string)(unsafe.Pointer(&b))
for name, value := range response.Header {
var hvaluecomp string = ""
for i := 0; i < len(value); i++ {
hvaluecomp += value[i]
}
response.Header.Add(name,hvaluecomp)
}
log.Printf("%s", s)
default:
startreader = response.Body
buf := new(bytes.Buffer)
buf.ReadFrom(startreader)
b := buf.Bytes()
s := *(*string)(unsafe.Pointer(&b))
for name, value := range response.Header {
fmt.Printf("%v: %v\n", name, value)
}
log.Printf("%s", s)
}
The issue with the above is, responses can only be read one time via the reader, after that the response cannot be read by the reverse proxy anymore and it makes the proxy response back to the browser nil =), so again I was met with failure. I cant imagine that the folks coding golang would have missed decoding gzip, just strange, it seems to be so simple.
So in the end, my question is, does DumpResponse give me the ability to decompress gzip so I can log the actual response instead of non utf8 characters? This does the log reader no good when debugging an issue for the production product. This would render the built in golang reverse proxy useless in my eyes and I would start development on my own.
The answer is to copy the stream, then you can use the second variable to re-post the stream back to the request.body object so you don't lose any data.
buf, _ := ioutil.ReadAll(response.Body)
responseuse1 := ioutil.NopCloser(bytes.NewBuffer(buf))
responsehold := ioutil.NopCloser(bytes.NewBuffer(buf))
Method to pull logging: extractLogging(responseuse1)
Push the hold back to the body so its untouched: response.Body = responsehold
return response
does DumpResponse give me the ability to decompress gzip
No it does not.
To me it seems as if the major problem is neither the reverse proxy nor DumpResponse but that you are trying to "log" binary data: Be it gzip, or other binary data like images. Just fix your logging logic: If the raw body is binary you should render some kind of representation or transformation of it. For gziped stuff gunzip it first (but this might still be binary "non utf8" data unsuitable for logging). Focus on the real problem: How to "log" binary data.

Verifying signature of payload in Go

I am verifying the identity of the sender of a piece of data. I am provided the RSA public key in a PEM format and I know the data is passed through the SHA256 hashing function. The equivalent verification on the node.js platform:
Ticket.prototype.verify = function (ticket) {
if (!ticket) return null;
var pubkey = fs.readFileSync('/etc/SCAMP/auth/ticket_verify_public_key.pem');
var parts = ticket.split(',');
if (parts[0] != '1') return null;
var sig = new Buffer(parts.pop().replace(/-/g,'+').replace(/_/g,'/'), 'base64');
var valid = crypto.createVerify('sha256').update( new Buffer(parts.join(',')) ).verify( pubkey, sig )
Which can verify:
1,3063,21,1438783424,660,1+20+31+32+34+35+36+37+38+39+40+41+42+43+44+46+47+48+50+53+56+59+60+61+62+67+68+69+70+71+75+76+80+81+82+86+87+88+102+104+105+107+109+110+122+124,PcFNyWjoz_iiVMgEe8I3IBfzSlUcqUGtsuN7536PTiBW7KDovIqCaSi_8nZWcj-j1dfbQRA8mftwYUWMhhZ4DD78-BH8MovNVucbmTmf2Wzbx9bsI-dmUADY5Q2ol4qDXG4YQJeyZ6f6F9s_1uxHTH456QcsfNxFWh18ygo5_DVmQQSXCHN7EXM5M-u2DSol9MSROeBolYnHZyE093LgQ2veWQREbrwg5Fcp2VZ6VqIC7yu6f_xYHEvU0-ZsSSRMAMUmhLNhmFM4KDjl8blVgC134z7XfCTDDjCDiynSL6b-D-
by splitting on the last ,. The left side of the split is the ticket data I care about, the right side is the signature which I need to verify before I can use the ticket data.
I have tried to port the logic to go:
func TestSigVerification(t *testing.T) {
block, _ := pem.Decode(signingPubKey)
if block == nil {
t.Errorf("expected to block to be non-nil CERTIFICATE", block)
}
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
t.Errorf("could not parse PKIXPublicKey: `%s`", key)
}
rsaPubKey, ok := key.(*rsa.PublicKey)
if !ok {
t.Errorf("couldn't cast to rsa.PublicKey!")
}
ticket,_ := ParseTicketBytes(fullTicketBytes)
h := sha256.New()
h.Write(ticketBytes)
digest := h.Sum(nil)
err = rsa.VerifyPKCS1v15(rsaPubKey, crypto.SHA256, digest, ticket.Signature)
if err != nil {
t.Errorf("could not verify ticket: `%s` (digest: `%v`)", err, digest )
}
}
But I'm pretty sure VerifyPKCS1v15 is not equivalent to node's crypto.createVerify and this test case fails. What should I be using? How can I use the public key to decrypt the signature and get the sha256? once I have the decrypted sha256 value I could just do a basic comparison with the sha256 I have generated.
Here's a runnable playground example: http://play.golang.org/p/COx2OG-AiA
Though I couldn't get it to work, I suspect the issue is that you'll need to convert the sig from base64 into bytes via the base64 encoding. See this example here:
http://play.golang.org/p/bzpD7Pa9mr (especially lines 23 to 28, where they have to encode the sig from bytes to base64 string to print it, then feed the byte version into the sig check, indicating that you have to use the byte version and not base64 string)
Which I stumbled across on this post:
Signing and decoding with RSA-SHA in GO
I've found that golang generally expects bytes everywhere in byte encoding. I tried to decode your sig string from base64 to bytes however, even after replacing the '-' with '+' and the '_' with '/' it still won't work, for reasons unknown to me:
http://play.golang.org/p/71IiV2z_t8
At the very least this seems to indicate that maybe your sig is bad? If it isn't valid base64? I think if you can find a way to solve this the rest should work!
I tried running your code and it doesn't look like your ticket is well formed. It's not long enough. Where did you get the value from?
Double check that ticket -- that may just be enough to get you going.

Receipt address won't show up in the email sent by golang smtp client

Here is the code snippet to send the email via a local postfix server:
from := r.FormValue("from")
to := strings.Split(r.FormValue("to"), ";")
body := r.FormValue("body")
mime := "MIME-version:1.0;\nContent-Type:text/html;charset=\"UTF-8\";\n\n"
subject := fmt.Sprintf("Subject: %s\n", r.FormValue("subject"))
msg := []byte(subject + mime + body)
err := smtp.SendMail("localhost:25", nil, from, to, msg)
The email was sent/received fine. However, it is missing the receipt address in the To field of received email. I also tried it on an exchange server. The receipt addresses are missing as well. Here is what it shows in the email source.
To: Undisclosed recipients:;
Any suggestions to fix it? thanks!
You're setting the values for the mail envelope, but you haven't put any headers in the email itself except for Subject:. You should also be using \r\n as a newline for email.
A minimal example might look like:
headers := make(map[string]string)
headers["Subject"] = "this is a test"
headers["From"] = "me#example.com"
headers["To"] = "you#example.com"
body := "hello,\nthis is a test"
var msg bytes.Buffer
for k, v := range headers {
msg.WriteString(k + ": " + v + "\r\n")
}
msg.WriteString("\r\n")
msg.WriteString(body)
Some other helpful stdlib packages:
net/textproto for MIME header handling
net/mail for address handling (though the package is really only for parsing email)
http://gopkg.in/gomail.v1 for a more complete solution (there are probably many others)

Resources