Generate ACCESS_TOKEN for http request

Generate ACCESS_TOKEN for http API

Mixin Network server provide API. Developer should put correct ACCESS_TOKEN in each http request. The ACCESS_TOKEN can be generated by following JWT spec.

each input parameter of JWT is:

"uid": App ID(the one in UUID format, not the 7000xxxx),
"sid": session ID,
"iat": current ts time,
"exp": expired time(200 means expired afte 200 seconds),
"jti": unique id in uuid format,
"sig": sha256(method+URI+body),
Calculate in python
jwtSig = hashlib.sha256(method + uri+body).hexdigest()
iat = datetime.datetime.utcnow()
exp = datetime.datetime.utcnow() + datetime.timedelta(seconds=200)
jwtResult = jwt.encode({'uid': mixin_client_id, 'sid':mixin_sessionid,'iat':iat,'exp': exp, 'jti':str(uuid.uuid1()),'sig':jwtSig}, private_key, algorithm='RS512')

Then put the token in http header like "Authorization": "Bearer ACCESS_TOKEN"

Http post in python
encoded = robot.genPOSTJwtToken_extConfig('/transfers', body_in_json, config)
r = requests.post('https://api.mixin.one/transfers', json = body, headers = {"Authorization":"Bearer " + encoded})

Extra secure requirement: encrypted_pin

Mixin messenger bot developer has to provide bot's asset pin when developer need to transfer bot's asset to other mixin network user. Asset pin and other content will be concatenated into a new content, then the content is encrypted and encrypted result should be place into http body.

The content to be encrypted is : pin + timestamp(little endian) + 8 byte counter

Time is timestamp array in little endian. Counter is an automatic counter. The counter used by bot/user in this call must be greater than last API call.

The algorithm to encrypt is AES.

Developer can get AES key by decrypting one parameter which is generated in dashboard : Pin_Token. Mixin server encrypt the AES key by RSA public key of bot/user, then encode the result into base64. To get the clear AES key, developer need to unpack the data from base64 format, then decrypt the data with private key.

The go lang code to generate encrypted_pin: here

golang-generate-encrypted_pin
/ Golang Example Code
func EncryptPIN(pin, pinToken, sessionId, privateKey string, iterator uint64) string {
keyBlock, _ := pem.Decode([]byte(privateKey))
key, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
if err != nil {
return ""
}
token, _ := base64.StdEncoding.DecodeString(pinToken)
keyBytes, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, key, token, []byte(sessionId))
if err != nil {
return ""
}
pinByte := []byte(pin)
timeBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(timeBytes, uint64(time.Now().Unix()))
pinByte = append(pinByte, timeBytes...)
iteratorBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(iteratorBytes, iterator)
pinByte = append(pinByte, iteratorBytes...)
padding := aes.BlockSize - len(pinByte)%aes.BlockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
pinByte = append(pinByte, padtext...)
block, _ := aes.NewCipher(keyBytes)
ciphertext := make([]byte, aes.BlockSize+len(pinByte))
iv := ciphertext[:aes.BlockSize]
io.ReadFull(rand.Reader, iv)
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], pinByte)
return base64.StdEncoding.EncodeToString(ciphertext)
}

Python code to generate encrypted_pin can be found following

generate encrypted pin in python
def genEncrypedPin_extConfig(self, ext_config):
privKeyObj = RSA.importKey(ext_config.private_key)
decoded_result = base64.b64decode(ext_config.mixin_pin_token)
decoded_result_inhexString = ":".join("{:02x}".format(ord(c)) for c in decoded_result)
cipher = PKCS1_OAEP.new(key = privKeyObj, hashAlgo = Crypto.Hash.SHA256, label = ext_config.mixin_pay_sessionid)
decrypted_msg = cipher.decrypt(decoded_result)
decrypted_msg_inhexString = ":".join("{:02x}".format(ord(c)) for c in decrypted_msg)
keyForAES = decrypted_msg
ts = int(time.time())
tszero = ts%0x100
tsone = (ts%0x10000) >> 8
tstwo = (ts%0x1000000) >> 16
tsthree = (ts%0x100000000) >> 24
tsstring = chr(tszero) + chr(tsone) + chr(tstwo) + chr(tsthree) + '\0\0\0\0'
counter = '\1\0\0\0\0\0\0\0'
toEncryptContent = ext_config.asset_pin + tsstring + tsstring
lenOfToEncryptContent = len(toEncryptContent)
toPadCount = 16 - lenOfToEncryptContent % 16
if toPadCount > 0:
paddedContent = toEncryptContent + chr(toPadCount) * toPadCount
else:
paddedContent = toEncryptContent
iv = Random.new().read(AES.block_size)
cipher = AES.new(keyForAES, AES.MODE_CBC, iv)
encrypted_result = cipher.encrypt(paddedContent)
msg = iv + encrypted_result
encrypted_pin = base64.b64encode(msg)
return encrypted_pin

and put the result in http body like following

Http post to transfer asset in python
body = {'asset_id': to_asset_id, 'counter_user_id':to_user_id, 'amount':str(to_asset_amount), 'pin':encrypted_pin, 'trace_id':str(uuid.uuid1()), 'memo':memo}
body_in_json = json.dumps(body)
encoded = robot.genPOSTJwtToken_extConfig('/transfers', body_in_json, config)
r = requests.post('https://api.mixin.one/transfers', json = body, headers = {"Authorization":"Bearer " + encoded})