Unfortunaltey there is no built-in brainpool support in go so I'm trying to get ECDH working with the help of a fork from keybase.
Maybe I'm making a newbie mistake here but by code is falling at the first hurdle (i.e. elliptic.Unmarshal returns nil) ?
package main
import (
"fmt"
"io/ioutil"
"log"
"encoding/pem"
"crypto/ecdsa"
"crypto/rand"
"github.com/keybase/go-crypto/brainpool"
"crypto/elliptic"
"crypto/sha256"
)
func main() {
fmt.Println("Hello")
content, err := ioutil.ReadFile("/tmp/TEST.pem")
if err != nil {
log.Fatal(err)
}
fmt.Printf("File contents: %s", content)
block, _ := pem.Decode(content)
if block == nil || block.Type != "PUBLIC KEY" {
log.Fatal("failed to decode PEM")
}
x,y := elliptic.Unmarshal(brainpool.P512r1(),block.Bytes)
if x == nil {
log.Fatal("failed to unmarshal")
}
pubb := ecdsa.PublicKey {brainpool.P512r1(),x,y}
priva, _ := ecdsa.GenerateKey(brainpool.P512r1(), rand.Reader)
b, _ := pubb.Curve.ScalarMult(pubb.X, pubb.Y, priva.D.Bytes())
shared1 := sha256.Sum256(b.Bytes())
fmt.Printf("\nShared key %x\n", shared1)
Update to show the test key:
-----BEGIN PUBLIC KEY-----
MIGbMBQGByqGSM49AgEGCSskAwMCCAEBDQOBggAEM/zOLT7nMN374k902oTRZXnG
97DPzvqi8QQJaKXcq1BSrU/sNeUhOi6Y+hBcr7ZE+WZDYNoQkaMNrdhF+3x1XGx7
BTBFL3U1w2ENmkIPiDa2o0Q/wpSOLo/RFabdK5Q3/yvq0hoSdXlpKozE7UTre5cU
bJcUzjXvs9KDLEq54Fs=
-----END PUBLIC KEY-----
You are trying to unmarshal the raw ASN.1 message, not the public key part. You should first unmarshal the ASN.1 block, then unmarshal the EC data.
I was wrong in my comment, Go doesn't support the Brainpool functions. So I borrowed some code from their X509 package to build a custom parser for the 6 curves in the Brainpool package.
Playground: https://play.golang.org/p/i-Zd4mTugjU
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"log"
"math/big"
"github.com/keybase/go-crypto/brainpool"
)
const ecKey = `-----BEGIN PUBLIC KEY-----
MIGbMBQGByqGSM49AgEGCSskAwMCCAEBDQOBggAEM/zOLT7nMN374k902oTRZXnG
97DPzvqi8QQJaKXcq1BSrU/sNeUhOi6Y+hBcr7ZE+WZDYNoQkaMNrdhF+3x1XGx7
BTBFL3U1w2ENmkIPiDa2o0Q/wpSOLo/RFabdK5Q3/yvq0hoSdXlpKozE7UTre5cU
bJcUzjXvs9KDLEq54Fs=
-----END PUBLIC KEY-----`
func main() {
block, _ := pem.Decode([]byte(ecKey))
if block == nil || block.Type != "PUBLIC KEY" {
log.Fatal("failed to decode PEM")
}
pub, err := parseBrainpoolPKIXPublicKey(block.Bytes)
if err != nil {
log.Fatalf("failed to parse key: %v", err)
}
pubb := ecdsa.PublicKey{Curve: brainpool.P512r1(), X: pub.X, Y: pub.Y}
priva, _ := ecdsa.GenerateKey(brainpool.P512r1(), rand.Reader)
b, _ := pubb.Curve.ScalarMult(pubb.X, pubb.Y, priva.D.Bytes())
shared1 := sha256.Sum256(b.Bytes())
fmt.Printf("\nShared key %x\n", shared1)
}
type publicKeyInfo struct {
Raw asn1.RawContent
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
type pkcs1PublicKey struct {
N *big.Int
E int
}
var (
oidNamedCurveBrainpoolP256r1 = asn1.ObjectIdentifier{1, 3, 36, 3, 3, 2, 8, 1, 1, 7}
oidNamedCurveBrainpoolP256t1 = asn1.ObjectIdentifier{1, 3, 36, 3, 3, 2, 8, 1, 1, 8}
oidNamedCurveBrainpoolP384r1 = asn1.ObjectIdentifier{1, 3, 36, 3, 3, 2, 8, 1, 1, 11}
oidNamedCurveBrainpoolP384t1 = asn1.ObjectIdentifier{1, 3, 36, 3, 3, 2, 8, 1, 1, 12}
oidNamedCurveBrainpoolP512r1 = asn1.ObjectIdentifier{1, 3, 36, 3, 3, 2, 8, 1, 1, 13}
oidNamedCurveBrainpoolP512t1 = asn1.ObjectIdentifier{1, 3, 36, 3, 3, 2, 8, 1, 1, 14}
)
func parseBrainpoolPKIXPublicKey(derBytes []byte) (pub *ecdsa.PublicKey, err error) {
var pki publicKeyInfo
if rest, err := asn1.Unmarshal(derBytes, &pki); err != nil {
if _, err := asn1.Unmarshal(derBytes, &pkcs1PublicKey{}); err == nil {
return nil, errors.New("failed to parse public key")
}
return nil, err
} else if len(rest) != 0 {
return nil, errors.New("trailing data after ASN.1 of public-key")
}
if !pki.Algorithm.Algorithm.Equal(asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}) {
return nil, errors.New("not an ECDSA public key")
}
return parseBrainpoolPublicKey(&pki)
}
func parseBrainpoolPublicKey(keyData *publicKeyInfo) (*ecdsa.PublicKey, error) {
asn1Data := keyData.PublicKey.RightAlign()
paramsData := keyData.Algorithm.Parameters.FullBytes
namedCurveOID := new(asn1.ObjectIdentifier)
rest, err := asn1.Unmarshal(paramsData, namedCurveOID)
if err != nil {
return nil, errors.New("failed to parse ECDSA parameters as named curve")
}
if len(rest) != 0 {
return nil, errors.New("trailing data after ECDSA parameters")
}
namedCurve := namedCurveFromOID(*namedCurveOID)
if namedCurve == nil {
return nil, errors.New("unsupported elliptic curve")
}
x, y := elliptic.Unmarshal(namedCurve, asn1Data)
if x == nil {
return nil, errors.New("failed to unmarshal elliptic curve point")
}
pub := &ecdsa.PublicKey{
Curve: namedCurve,
X: x,
Y: y,
}
return pub, nil
}
func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
switch {
case oid.Equal(oidNamedCurveBrainpoolP256r1):
return brainpool.P256r1()
case oid.Equal(oidNamedCurveBrainpoolP256t1):
return brainpool.P256t1()
case oid.Equal(oidNamedCurveBrainpoolP384r1):
return brainpool.P384r1()
case oid.Equal(oidNamedCurveBrainpoolP384t1):
return brainpool.P384t1()
case oid.Equal(oidNamedCurveBrainpoolP512r1):
return brainpool.P512r1()
case oid.Equal(oidNamedCurveBrainpoolP512t1):
return brainpool.P512t1()
}
return nil
}
Related
I'am having a PCollection from which I need to choose n largest rows. I'am trying to create a Dataflow pipeline using Go and stuck at this.
package main
import (
"context"
"flag"
"fmt"
"github.com/apache/beam/sdks/v2/go/pkg/beam"
"github.com/apache/beam/sdks/v2/go/pkg/beam/log"
"github.com/apache/beam/sdks/v2/go/pkg/beam/x/beamx"
)
type User struct {
Name string
Age int
}
func printRow(ctx context.Context, list User) {
fmt.Println(list)
}
func main() {
flag.Parse()
beam.Init()
ctx := context.Background()
p := beam.NewPipeline()
s := p.Root()
var userList = []User{
{"Bob", 5},
{"Adam", 8},
{"John", 3},
{"Ben", 1},
{"Jose", 1},
{"Bryan", 1},
{"Kim", 1},
{"Tim", 1},
}
initial := beam.CreateList(s, userList)
pc2 := beam.ParDo(s, func(row User, emit func(User)) {
emit(row)
}, initial)
beam.ParDo0(s, printRow, pc2)
if err := beamx.Run(ctx, p); err != nil {
log.Exitf(ctx, "Failed to execute job: %v", err)
}
}
From the above code I need to choose top 5 rows based on User.Age
I found the link top package which has a function does the same but it says it returns a single element PCollection. How is it different?
package main
import (
"context"
"flag"
"fmt"
"github.com/apache/beam/sdks/v2/go/pkg/beam"
"github.com/apache/beam/sdks/v2/go/pkg/beam/log"
"github.com/apache/beam/sdks/v2/go/pkg/beam/transforms/top"
"github.com/apache/beam/sdks/v2/go/pkg/beam/x/beamx"
)
func init() {
beam.RegisterFunction(less)
}
type User struct {
Name string
Age int
}
func printRow(ctx context.Context, list User) {
fmt.Println(list)
}
func less(a, b User) bool {
return a.Age < b.Age
}
func main() {
flag.Parse()
beam.Init()
ctx := context.Background()
p := beam.NewPipeline()
s := p.Root()
var userList = []User{
{"Bob", 5},
{"Adam", 8},
{"John", 3},
{"Ben", 1},
{"Jose", 1},
{"Bryan", 1},
{"Kim", 1},
{"Tim", 1},
}
initial := beam.CreateList(s, userList)
best := top.Largest(s, initial, 5, less)
pc2 := beam.ParDo(s, func(row User, emit func(User)) {
emit(row)
}, best)
beam.ParDo0(s, printRow, pc2)
if err := beamx.Run(ctx, p); err != nil {
log.Exitf(ctx, "Failed to execute job: %v", err)
}
}
I added the function to select the top 5 rows like above, but I get an error []main.User is not assignable to main.User
I need the PCollection in the same format as before since I have further processing to do. I suspect this is because the top.Largest function is returning a single-element PCollection. Any ideas on how I can convert the format?
best PCollection is []User
so try...
pc2 := beam.ParDo(s, func(rows []User, emit func(User)) {
for _, row := range rows {
emit(row)
}
}, best)
It is not clear on how to get the value of the functions used inside of a form using https://github.com/rivo/tview/.
package main
import (
"fmt"
"github.com/rivo/tview"
)
func main() {
app := tview.NewApplication()
form := tview.NewForm()
form.SetBorder(true).SetTitle("Enter some data").SetTitleAlign(tview.AlignLeft)
form.AddDropDown("Title", []string{"Mr.", "Ms.", "Mrs.", "Dr.", "Prof."}, 0, nil)
form.AddInputField("Value1", "", 0, nil, nil)
form.AddInputField("Value2", "", 0, nil, nil)
form.AddInputField("Value3", "", 0, nil, nil)
form.AddInputField("Value4", "", 0, nil, nil)
form.AddButton("OK", func() { app.Stop() })
if err := app.SetRoot(form, true).SetFocus(form).Run(); err != nil {
panic(err)
}
fmt.Printf("%s\n", form.GetFormItem(0).(*tview.DropDown))
}
Is it possible to convert the output of the dropdown to text?
To get the selected option (both index and text), use the DropDown.GetCurrentOption() method:
i, s := form.GetFormItem(0).(*tview.DropDown).GetCurrentOption()
fmt.Printf("%d %s\n", i, s)
Example output:
2 Mrs.
I have a method Deduplicate that returns deduplicated copy of passed in slice as an interface{}. Is there a way to cast returned by this method interface{} value to the same type as I passed in this method without writing it explicitly? For example, if I change myStruct.RelatedIDs type from []int to []uint it will prevent code from compiling.
https://play.golang.org/p/8OT4xYZuwEn
package main
import (
"fmt"
"reflect"
)
type myStruct struct {
ID int
RelatedIDs []int
}
func main() {
s := &myStruct{
ID: 42,
RelatedIDs: []int{1, 1, 2, 3},
}
v, _ := Deduplicate(s.RelatedIDs)
s.RelatedIDs = v.([]int) // << can I assert type dynamically here?
// s.RelatedIDs = v.(reflect.TypeOf(s.RelatedIDs)) // does not work
fmt.Printf("%#v\n", s.RelatedIDs)
}
func Deduplicate(slice interface{}) (interface{}, error) {
if reflect.TypeOf(slice).Kind() != reflect.Slice {
return nil, fmt.Errorf("slice has wrong type: %T", slice)
}
s := reflect.ValueOf(slice)
res := reflect.MakeSlice(s.Type(), 0, s.Len())
seen := make(map[interface{}]struct{})
for i := 0; i < s.Len(); i++ {
v := s.Index(i)
if _, ok := seen[v.Interface()]; ok {
continue
}
seen[v.Interface()] = struct{}{}
res = reflect.Append(res, v)
}
return res.Interface(), nil
}
Try this
package main
import (
"fmt"
"reflect"
)
type myStruct struct {
ID int
RelatedIDs []int
}
func main() {
s := &myStruct{
ID: 42,
RelatedIDs: []int{1, 1, 2, 3},
}
err := Deduplicate(&s.RelatedIDs)
fmt.Println(err)
// s.RelatedIDs = v.([]int) // << can I assert type dynamically here?
// s.RelatedIDs = v.(reflect.TypeOf(s.RelatedIDs)) // does not work
fmt.Printf("%#v\n", s.RelatedIDs)
}
func Deduplicate(slice interface{}) error {
rts := reflect.TypeOf(slice)
rtse := rts.Elem()
if rts.Kind() != reflect.Ptr && rtse.Kind() != reflect.Slice {
return fmt.Errorf("slice has wrong type: %T", slice)
}
rvs := reflect.ValueOf(slice)
rvse := rvs.Elem()
seen := make(map[interface{}]struct{})
var e int
for i := 0; i < rvse.Len(); i++ {
v := rvse.Index(i)
if _, ok := seen[v.Interface()]; ok {
continue
}
seen[v.Interface()] = struct{}{}
rvse.Index(e).Set(v)
e++
}
rvse.SetLen(e)
rvs.Elem().Set(rvse)
return nil
}
https://play.golang.org/p/hkEW4u1aGUi
with future generics, it might look like this https://go2goplay.golang.org/p/jobI5wKR8fU
For completeness, here is a generic version (Playground), which doesn't require reflection. Only open in February 2022! The usual caveats about NaN apply, though.
package main
import (
"fmt"
)
func main() {
s := []int{1, 1, 2, 3}
res := Deduplicate(s)
fmt.Printf("%#v\n", res)
}
func Deduplicate[T comparable](s []T) []T {
seen := make(map[T]struct{})
res := make([]T, 0, len(s))
for _, elem := range s {
if _, exists := seen[elem]; exists {
continue
}
seen[elem] = struct{}{}
res = append(res, elem)
}
return res
}
Output:
[]int{1, 2, 3}
doc: https://developers.google.com/ad-exchange/rtb/response-guide/decrypt-price#sample_code
There is no official "Double Click Crypto" example code in golang, so I try to implement by myself. But I can't pass the test in doc. Please help me!
skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o= // Encryption key (e_key)
arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo= // Integrity key (i_key)
WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA // 100 CPI micros
WEp8sQAAAACwF6CtLJrXSRFBM8UiTTIyngN-og // 1900 CPI micros
WEp8nQAAAAADG-y45xxIC1tMWuTjzmDW6HtroQ // 2700 CPI micros
Here is my code:
package main
// https://developers.google.com/ad-exchange/rtb/response-guide/decrypt-price?hl=zh-CN
import (
"bytes"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/binary"
"fmt"
)
var base64Codec = base64.URLEncoding.WithPadding(base64.NoPadding)
func safeXORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
for i := 0; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}
func EncryptPrice(eKey []byte, iKey []byte, price uint64, iv []byte) (finalMessage string, err error) {
if len(iv) != 16 {
err = fmt.Errorf("len(iv) = %d != 16", len(iv))
return
}
h1 := hmac.New(sha1.New, eKey)
h1.Write(iv)
pad := h1.Sum(nil)[:8]
priceBytes := make([]byte, 8)
binary.BigEndian.PutUint64(priceBytes, price)
encPrice := make([]byte, 8)
n := safeXORBytes(encPrice, priceBytes, pad)
if n != 8 {
err = fmt.Errorf("safeXORBytes n != %d", n)
return
}
h2 := hmac.New(sha1.New, iKey)
h2.Write(priceBytes)
h2.Write(iv)
signature := h2.Sum(nil)[:4]
finalMessage = base64Codec.EncodeToString(append(append(iv, encPrice...), signature...))
return
}
func DecryptPrice(eKey []byte, iKey []byte, finalMessage string) (price uint64, err error) {
finalMessageBytes, err := base64Codec.DecodeString(finalMessage)
if err != nil {
return
}
if len(finalMessageBytes) != 28 {
err = fmt.Errorf("len(finalMessageBytes) = %d != 28", len(finalMessageBytes))
return
}
iv := finalMessageBytes[:16]
encPrice := finalMessageBytes[16:24]
signature := finalMessageBytes[24:]
h1 := hmac.New(sha1.New, eKey)
h1.Write(iv)
pad := h1.Sum(nil)[:8]
priceBytes := make([]byte, 8)
n := safeXORBytes(priceBytes, encPrice, pad)
if n != 8 {
err = fmt.Errorf("safeXORBytes n != %d", n)
return
}
h2 := hmac.New(sha1.New, iKey)
h2.Write(priceBytes)
h2.Write(iv)
confSignature := h2.Sum(nil)[:4]
if bytes.Compare(confSignature, signature) != 0 {
err = fmt.Errorf("sinature mismatch: confSignature = %s, sinature = %s", confSignature, signature)
return
}
price = binary.BigEndian.Uint64(priceBytes)
return
}
package main
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestEncryptPrice(t *testing.T) {
eKeyBase64Encoded := "skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o"
eKey, err := base64Codec.DecodeString(eKeyBase64Encoded)
assert.Nil(t, err)
iKeyBase64Encoded := "arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo"
iKey, err := base64Codec.DecodeString(iKeyBase64Encoded)
assert.Nil(t, err)
finalMessage, err := EncryptPrice(eKey, iKey, uint64(100), []byte{88, 74, 124, 193, 0, 0, 0, 0, 103, 21, 222, 68, 144, 29, 164, 215})
assert.Nil(t, err)
assert.Equal(t, "WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA", finalMessage)
}
func TestDecryptPrice(t *testing.T) {
eKeyBase64Encoded := "skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o"
eKey, err := base64Codec.DecodeString(eKeyBase64Encoded)
assert.Nil(t, err)
iKeyBase64Encoded := "arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo"
iKey, err := base64Codec.DecodeString(iKeyBase64Encoded)
assert.Nil(t, err)
price, err := DecryptPrice(eKey, iKey, "WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA")
assert.Nil(t, err)
assert.Equal(t, uint64(100), price)
}
test result:
--- FAIL: TestEncryptPrice (0.00s)
Error Trace: price_test.go:19
Error: Not equal:
expected: "WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA"
actual: "WEp8wQAAAABnFd5EkB2k1wf7jAcG7gZ9tPUnAA"
--- FAIL: TestDecryptPrice (0.00s)
Error Trace: price_test.go:32
Error: Expected nil, but got: &errors.errorString{s:"sinature mismatch: confSignature = \xbe\xaa\xf6h, sinature = T\xe7\x86\xcc"}
Error Trace: price_test.go:33
Error: Not equal:
expected: 0x64
actual: 0x0
FAIL
this code can pass the test in https://github.com/google/openrtb-doubleclick/blob/0.9.0/doubleclick-core/src/test/java/com/google/doubleclick/crypto/DoubleClickCryptoTest.java
, so I guess that the encrypted price in the doc are not excrypted by the e_key and i_key in the doc.
I met the same problem today. Finally, I found the bug was that 'ekey' and 'ikey' did not need to base64Codec.DecodeString, just use []byte(eKeyBase64Encoded).
eKeyBase64Encoded := "skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o"
eKey := []byte(eKeyBase64Encoded)
assert.Nil(t, err)
iKeyBase64Encoded := "arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo"
iKey :=[]byte(iKeyBase64Encoded)
assert.Nil(t, err)
price, err := DecryptPrice(eKey, iKey, "WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA")
....
The encrypted price that are listed in this document https://developers.google.com/authorized-buyers/rtb/response-guide/decrypt-price have been encoded using the keys they showed above the prices. The examples are correct. I wrote a small Go package that provides a Decrypt function, I might add an encrypt one soon: https://github.com/matipan/doubleclick. Below I leave an example and code snippets for easy access.
First, to be able to parse the keys we need to respect the web-safe base64 decoding, which means we need to use base64.URLEncoding! to decode the keys:
// ParseKeys parses the base64 web-safe encoded keys as explained in Google's documentation:
// https://developers.google.com/authorized-buyers/rtb/response-guide/decrypt-price
func ParseKeys(ic, ec []byte) (icKey []byte, ecKey []byte, err error) {
icKey = make([]byte, base64.URLEncoding.DecodedLen(len([]byte(ic))))
n, err := base64.URLEncoding.Decode(icKey, []byte(ic))
if err != nil {
return nil, nil, fmt.Errorf("%w: could not decode price integrity key", err)
}
icKey = icKey[:n]
ecKey = make([]byte, base64.URLEncoding.DecodedLen(len([]byte(ec))))
n, err = base64.URLEncoding.Decode(ecKey, []byte(ec))
if err != nil {
return nil, nil, fmt.Errorf("%w: could not decode price encryption key", err)
}
ecKey = ecKey[:n]
return icKey, ecKey, nil
}
We can call this code and decrypt the keys they show:
icKey, ecKey, err = ParseKeys([]byte("arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo="), []byte("skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o="))
The function to decrypt the price with a bit more error handling is:
// ErrInvalidPrice is the error returned when the price parsed
// by DecryptPrice is not correct.
var ErrInvalidPrice = errors.New("price is invalid")
// DecryptPrice decrypts the price with google's doubleclick cryptography encoding.
// encPrice is an unpadded web-safe base64 encoded string according to RFC 3548.
// https://developers.google.com/authorized-buyers/rtb/response-guide/decrypt-price
func DecryptPrice(icKey, ecKey, encPrice []byte) (uint64, error) {
if len(icKey) == 0 || len(ecKey) == 0 {
return 0, errors.New("encryption and integrity keys are required")
}
if len(encPrice) != 38 {
return 0, fmt.Errorf("%w: invalid length, expected 28 got %d", ErrInvalidPrice, len(encPrice))
}
dprice := make([]byte, base64.RawURLEncoding.DecodedLen(len(encPrice)))
n, err := base64.RawURLEncoding.Decode(dprice, encPrice)
if err != nil {
return 0, fmt.Errorf("%w: invalid base64 string", err)
}
dprice = dprice[:n]
if len(dprice) != 28 {
return 0, fmt.Errorf("%w: invalid decoded price length. Expected 28 got %d", ErrInvalidPrice, len(dprice))
}
// encrypted price is composed of parts of fixed lenth. We break it up according to:
// {initialization_vector (16 bytes)}{encrypted_price (8 bytes)}{integrity (4 bytes)}
iv, p, sig := dprice[0:16], dprice[16:24], dprice[24:]
h := hmac.New(sha1.New, ecKey)
n, err = h.Write(iv)
if err != nil || n != len(iv) {
return 0, fmt.Errorf("%w: could not write hmac hash for iv. err=%s, n=%d, len(iv)=%d", ErrInvalidPrice, err, n, len(iv))
}
pricePad := h.Sum(nil)
price := safeXORBytes(p, pricePad)
if price == nil {
return 0, fmt.Errorf("%w: price xor price_pad failed", ErrInvalidPrice)
}
h = hmac.New(sha1.New, icKey)
n, err = h.Write(price)
if err != nil || n != len(price) {
return 0, fmt.Errorf("%w: could not write hmac hash for price. err=%s, n=%d, len(price)=%d", ErrInvalidPrice, err, n, len(price))
}
n, err = h.Write(iv)
if err != nil || n != len(iv) {
return 0, fmt.Errorf("%w: could not write hmac hash for iv. err=%s, n=%d, len(iv)=%d", ErrInvalidPrice, err, n, len(iv))
}
confSig := h.Sum(nil)[:4]
if bytes.Compare(confSig, sig) != 0 {
return 0, fmt.Errorf("%w: integrity of price is not valid", ErrInvalidPrice)
}
return binary.BigEndian.Uint64(price), nil
}
func safeXORBytes(a, b []byte) []byte {
n := len(a)
if len(b) < n {
n = len(b)
}
if n == 0 {
return nil
}
dst := make([]byte, n)
for i := 0; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return dst
}
We can now call this code using the example of 1900 micros: YWJjMTIzZGVmNDU2Z2hpN7fhCuPemC32prpWWw:
price, err := DecryptPrice(icKey, ecKey, []byte("YWJjMTIzZGVmNDU2Z2hpN7fhCuPemC32prpWWw"))
And price will be 1900
like http://play.golang.org/p/fD7mx2k4Yc
window rdp password encrypted http://www.remkoweijnen.nl/blog/2007/10/18/how-rdp-passwords-are-encrypted/
package main
import (
"fmt"
"log"
"syscall"
"unsafe"
)
const (
CRYPTPROTECT_UI_FORBIDDEN = 0x1
)
var (
dllcrypt32 = syscall.NewLazyDLL("Crypt32.dll")
dllkernel32 = syscall.NewLazyDLL("Kernel32.dll")
procEncryptData = dllcrypt32.NewProc("CryptProtectData")
procDecryptData = dllcrypt32.NewProc("CryptUnprotectData")
procLocalFree = dllkernel32.NewProc("LocalFree")
)
type DATA_BLOB struct {
cbData uint32
pbData *byte
}
func NewBlob(d []byte) *DATA_BLOB {
if len(d) == 0 {
return &DATA_BLOB{}
}
return &DATA_BLOB{
pbData: &d[0],
cbData: uint32(len(d)),
}
}
func (b *DATA_BLOB) ToByteArray() []byte {
d := make([]byte, b.cbData)
copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
return d
}
func Encrypt(data []byte) ([]byte, error) {
var outblob DATA_BLOB
r, _, err := procEncryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outblob)))
if r == 0 {
return nil, err
}
defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData)))
return outblob.ToByteArray(), nil
}
func Decrypt(data []byte) ([]byte, error) {
var outblob DATA_BLOB
r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outblob)))
if r == 0 {
return nil, err
}
defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData)))
return outblob.ToByteArray(), nil
}
func main() {
const secret = "MYpasswd"
enc, err := Encrypt([]byte(secret))
if err != nil {
log.Fatalf("Encrypt failed: %v", err)
}
dec, err := Decrypt(enc)
if err != nil {
log.Fatalf("Decrypt failed: %v", err)
}
if string(dec) != secret {
log.Fatalf("decrypted secret \"%s\" does not match to \"%s\".", dec, secret)
}
fmt.Println(fmt.Sprintf("%x", enc))
fmt.Println(string(dec))
}
out: 01000000d08c9ddf0115d1118c7a00c04fc297eb01000000de7c90fbe3c9854381f0a0ffe1d496f3000000000200000000001066000000010000200000000790b641e1a9d4bfe54d81966c4d7aaeabf19b63c36dff42668e3b256edbeed8000000000e8000000002000020000000d6385d3352d5a4b011e171ab25b30271e73a4ddc0b9f9bfb8ecd13f230362a0110000000da71663217c163d7ab77231282e7d8d64000000025fbcbb72efcdc711f3a74c38bddbf0b71538f0ffe27d133c0c5cd2434f88d55d924f598ac2f94758d66a448682ed841fb56ce8c9de38601dcce6bd42aa41fbb
MYpasswd
create tmp.rdp
screen mode id:i:1
....
winposstr:s:0,1,153,64,953,664
username:s:{{username}}
domain:s:
password 51:b:01000000d08c9ddf0115d1118c7a00c04fc297eb0100000............
disable wallpaper:i:1
disable full window drag:i:1
final:
mstsc.exe tmp.rdp
But Login failed
python "win32crypt.CryptProtectData" is work.
pwdHash = win32crypt.CryptProtectData(u"MYpasswd", u'pws', None, None, None, 0)
enc_password = binascii.hexlify(pwdHash)
make an addition for the not-detailed answer above. According #Fuqiang's answer, just transfer the encoding of the plain string to UTF-16LE before encrypting, and it finally works. So it looks like this:
....
func convertToUTF16LittleEndianBytes(s string) []byte {
u := utf16.Encode([]rune(s))
b := make([]byte, 2*len(u))
for index, value := range u {
binary.LittleEndian.PutUint16(b[index*2:], value)
}
return b
}
func main() {
const secret = "MYpasswd"
s := convertToUTF16LittleEndianBytes(secret)
enc, err := Encrypt(s)
if err != nil {
log.Fatalf("Encrypt failed: %v", err)
}
...
}
And after decrypting, you have to decode the decrypted string from utf-16le before you comparing or using it, don't directly transfer the utf-16le char to string by string(dec).
The issue has been solved.
secret = "MYpasswd"
string must use UTF-16LE encode.