diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md
index 4b14306bfbdc4a3ae61d76e24bbdf599400c248a..bdda49c6ceb976d13893df6e7992d7b7cbbc1cb1 100644
--- a/CHANGELOG.fr.md
+++ b/CHANGELOG.fr.md
@@ -9,12 +9,15 @@ et ce projet adhère au [versionnage sémantique](https://semver.org/spec/v2.0.0
 ## Evolutions probable / Roadmap :
 - GraphQL stuff
 - @@@@ comme séparateur entre identifiant secret et mdp pour la génération de combinaison à tester (usage principal Gsper)
-- supprimer automatiquement le code inutile dans les lib (Tree Shaking)
-- chiffrer déchiffrer des messages
-- lire et écrire des messages au format cesium+
+
 
 ## [Non-publié/Non-Stabilisé] (par [1000i100])
 
+## [Version 3.4.0] - 2022-11-15 (par [1000i100])
+### Ajouté
+- crypto.textEncrypt(jsonMessage, senderPrivateKey, receiverPubKey) retourne un json format cesium+ avec `jsonMessage.title` et `jsonMessage.content` chiffrés.
+- textDecrypt(jsonMessage, receiverPrivateKey)) retourne en json les champs `title` et `content` déchiffrés à partir d'un message chiffré format cesium+.
+
 ## [Version 3.3.3] - 2022-11-15 (par [1000i100])
 ### Corrections
 - les versions 3.3.x antérieur à celle-ci, cherchaient à importer la lib crypto de node depuis le navigateur c'est corrigé.
@@ -98,8 +101,9 @@ et ce projet adhère au [versionnage sémantique](https://semver.org/spec/v2.0.0
 - intégration des librairies de crypto nécessaires
 - calcul de la clef publique correspondant à chaque combinaison de secrets saisie, et comparaison à la clef publique de référence.
 
-[Non-publié/Non-Stabilisé]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.3.3...master
+[Non-publié/Non-Stabilisé]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.4.0...main
 
+[Version 3.4.0]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.3.3...v3.4.0
 [Version 3.3.3]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.3.2...v3.3.3
 [Version 3.3.2]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.3.1...v3.3.2
 [Version 3.3.1]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.3.0...v3.3.1
diff --git a/npm/package.json b/npm/package.json
index 3dec726107294b9e8134d8de184b1a0de4aa7887..df79b1f275a7c7fba6b44887fcd9253987357f0a 100644
--- a/npm/package.json
+++ b/npm/package.json
@@ -1,6 +1,6 @@
 {
   "name": "g1lib",
-  "version": "3.3.3",
+  "version": "3.4.0",
   "description": "An ubiquitous static javascript toolbox lib for Ǧ1 / Duniter ecosystem with reliability in mind.",
   "main": "nodejs/all.mjs",
 	"browser": "browser/all.mjs",
diff --git a/src/crypto.mjs b/src/crypto.mjs
index 32067a98bdf11a6e67d75d487d40908827e2b56e..cac959e77e4e88e45542717ed928ed6a2861b609 100644
--- a/src/crypto.mjs
+++ b/src/crypto.mjs
@@ -5,7 +5,7 @@ import sha256 from '../node_modules/js-sha256/src/sha256.mjs';
 import nacl from '../generated/vendors/nacl.mjs';
 import {convertPublicKey, convertSecretKey} from '../node_modules/ed2curve/src/index.mjs';
 //import {convertPublicKey} from '../node_modules/ed2curve-esm/dist-src/index.mjs';
-import {b58, b64} from './basex.mjs';
+import {b16, b58, b64} from './basex.mjs';
 import {random, ed25519} from './context-dependant/generics.mjs';
 nacl.setPRNG(random);
 export const mockRandom = nacl.setPRNG;
@@ -157,9 +157,9 @@ export async function signDocument(unsignedDocument, secretKey) {
 	return `${unsignedDocument.trim()}\n${signHash}`;
 }
 
-export async function sign(str, secretKey, outputFormat = 'b64') {
+export function sign(str, secretKey, outputFormat = 'b64') {
 	const encoder = new TextEncoder();
-	const raw = await rawSign(encoder.encode(str), b58secretKey2bin(secretKey).slice(0, 32));
+	const raw = rawSign(encoder.encode(str), b58secretKey2bin(secretKey).slice(0, 32));
 	switch (outputFormat.toLocaleLowerCase()) {
 		case 'raw':
 		case 'array':
@@ -174,7 +174,58 @@ export async function sign(str, secretKey, outputFormat = 'b64') {
 	}
 }
 
-export async function rawSign(uint8Array, rawSeed) {
-	const keys = await seed2keyPair(rawSeed);
+export function rawSign(uint8Array, rawSeed) {
+	const keys = seed2keyPair(rawSeed);
 	return nacl.sign.detached(uint8Array, keys.secretKey);
 }
+
+const NONCE_BYTES = 24;
+const PUBKEY_BYTES = 32;
+
+export function typedStrOrBin2Bin(tStrOrBin,type2binFunc){
+	return (typeof tStrOrBin === 'string')?type2binFunc(tStrOrBin):tStrOrBin;
+}
+export function strOrBin2bin(strOrBin){
+	const encoder = new TextEncoder();
+	return (typeof strOrBin === 'string')?encoder.encode(strOrBin):strOrBin;
+}
+export function b64orBin2bin(b64orBin){
+	return typedStrOrBin2Bin(b64orBin,b64.decode);
+}
+export function b58orBin2bin(b58orBin){
+	return typedStrOrBin2Bin(b58orBin,b58.decode);
+}
+export function bin2str(bin){
+	const decoder = new TextDecoder();
+	return decoder.decode(bin);
+}
+function secKey2bin(secretKey){
+	return b58orBin2bin(secretKey).slice(0,PUBKEY_BYTES);
+}
+
+
+export function textEncrypt(jsonMessage, senderPrivateKey, receiverPubKey, nowInSecond=0, b58nonce=''){
+	const bNonce = b58nonce?b58.decode(b58nonce):nacl.randomBytes(NONCE_BYTES);
+	console.log("Défini : ", receiverPubKey)
+	const bReceiverPubKey = convertPublicKey(pubKey2bin(receiverPubKey));
+	const bSenderPrivateKey = convertSecretKey(secKey2bin(senderPrivateKey));
+	const encrypt = utf8text => b64.encode(nacl.box(strOrBin2bin(utf8text),bNonce,bReceiverPubKey,bSenderPrivateKey));
+	const b58sendPubKey = b58.encode(seed2keyPair(secKey2bin(senderPrivateKey)).publicKey);
+	const sTimestamp = nowInSecond || Math.trunc(Date.now()/1000);
+	const res = {nonce:b58.encode(bNonce),issuer:b58sendPubKey,recipient:receiverPubKey,time:sTimestamp,version:2};
+	if(jsonMessage.title) res.title = encrypt(jsonMessage.title);
+	if(jsonMessage.content) res.content = encrypt(jsonMessage.content);
+	res.hash = b16.encode(sha256Instance.digest(JSON.stringify(res))).toUpperCase();
+	res.signature = sign(res.hash,senderPrivateKey)
+	return res;
+}
+export function textDecrypt(jsonMessage, receiverPrivateKey){
+	const bNonce = b58.decode(jsonMessage.nonce);
+	const bReceiverPrivateKey = convertSecretKey(secKey2bin(receiverPrivateKey));
+	const bSenderPubKey = convertPublicKey(pubKey2bin(jsonMessage.issuer));
+	const decrypt = b64text => bin2str(nacl.box.open(b64.decode(b64text),bNonce,bSenderPubKey,bReceiverPrivateKey));
+	const res = {};
+	if(jsonMessage.content) res.content = decrypt(jsonMessage.content);
+	if(jsonMessage.title) res.title = decrypt(jsonMessage.title);
+	return res;
+}
diff --git a/src/crypto.test.mjs b/src/crypto.test.mjs
index 65e87ef8bc41d127d0f6415fa09820a3eaff540a..2c45600463d3c1169e7371f44ccf5de969b144dd 100644
--- a/src/crypto.test.mjs
+++ b/src/crypto.test.mjs
@@ -28,7 +28,15 @@ test('b58 sign string', async t => t.is(await app.sign(unsignedDocument, secretK
 test('raw sign string', async t => t.is((await app.sign(unsignedDocument, secretKey, 'raw'))[0], 27));
 test('array sign string', async t => t.is((await app.sign(unsignedDocument, secretKey, 'Array'))[0], 27));
 test('uint8array sign string', async t => t.is((await app.sign(unsignedDocument, secretKey, 'uint8array'))[0], 27));
-test('sign throw for bad output format', async t => t.throwsAsync(() => app.sign(unsignedDocument, secretKey, 'whattt ?')));
+test('sign throw for bad output format', t => t.throws(() => app.sign(unsignedDocument, secretKey, 'whattt ?')));
+
+//test('signOnly(message, privateKey)', t => t.is(app.signOnly('a message', secretKey), 'signature'));
+//test('signOnly(message, privateKey, returnFormat=uint8array)', t => t.is(app.signOnly([0,1,2,3], secretKey), [0,1,2,3]));
+//test('signDocument or signMessage(message, privateKey)', t => t.is(app.sign('a message', secretKey), 'message avec sa signature'));
+//test('sign(uint8array, privateKey, returnFormat=uint8array)', async t => t.is(await app.sign([0,1,2,3], secretKey,'uint8array'), [0,0,0,0]));
+//test('checkSign(message, issuerPubKey) succeed', t => t.is(app.checkSign('a message', secretKey), true));
+//test('checkSign(message, issuerPubKey) fail', t => t.is(app.checkSign([0,1,2,3], secretKey,'uint8array'), false));
+
 
 test('b58 should decode/encode well', t => t.is(app.b58.encode(app.b58.decode(pubKey)), pubKey));
 test('b58 on pubKey with leading 1', t => t.is(app.b58.encode(app.b58.decode('12BjyvjoAf5qik7R8TKDJAHJugsX23YgJGi2LmBUv2nx')), '12BjyvjoAf5qik7R8TKDJAHJugsX23YgJGi2LmBUv2nx'));
@@ -127,3 +135,34 @@ test('checkKey accept valid binary pubKey', t => t.true(app.checkKey(app.b58.dec
 
 test("isPubKey return true when checkKey return true ", (t) => t.true(app.isPubKey(pubKey)));
 test("isPubKey return false when checkKey throw an error", (t) => t.false(app.isPubKey(pubKey.replace(/6/,'9'))));
+
+app.mockRandom((u8a, n)=>{
+	if(!u8a && !n) return 0.5;
+	if(!n && typeof u8a === "number"){n = u8a;u8a = new Uint8Array(n);}
+	for (let i = 0; i < n; i++) u8a[i] = 5;
+	return u8a;
+});
+
+const user1 = await app.idSecPass2cleanKeys('1','1');
+const user2 = await app.idSecPass2cleanKeys('2','2');
+const timestampInSeconds = 1222111000;
+const sampleMessage = {title:"Mon Titre",content:"Mon message"};
+const cryptedMessage =    {
+	content: 'G0ZAirsaoeAt/pOcsjg0milDkeBu8Uo3BgrZ',
+	hash: '65EC1622D2C38422EA21124397AB7A9F36A94BB6FC2C474C706ECAC33658114C',
+	issuer: 'BUhLyJT17bzDVXW66xxfk1F7947vytmwJVadTaWb8sJS',
+	nonce: 'TYPjCiGbKiwP6r12cdkmVjySbQpSHavp',
+	recipient: '7nge6q7F4k7FQ2q4FRMMPvt2tK7AEx8gNNRLr6LwZN38',
+	signature: 'u4m2YHp5nVxcXxMjQvv9H4kfSV8Q3/Jh68jHplbCjizXlA9XXksu+YuFrS9B5KuQ2HX68g9+kRdLoCspMuWtBg==',
+	time: timestampInSeconds,
+	title: 'yV5cDZCuJWqMBXsFmEm1FSlDkeBX/U02Ag==',
+	version: 2
+};
+
+test('textEncrypt(jsonMessage, senderPrivateKey, receiverPubKey) encrypt to cesium+ message format',
+		t => t.deepEqual(
+			app.textEncrypt(sampleMessage, user1.secretKey, user2.publicKey,timestampInSeconds),
+			cryptedMessage)
+);
+test('textDecrypt(jsonMessage, receiverPrivateKey)', t => t.deepEqual(app.textDecrypt(cryptedMessage, user2.secretKey),
+	{title:"Mon Titre",content:"Mon message"}));