Skip to content

Instantly share code, notes, and snippets.

@magnusnordlander
Last active July 19, 2023 10:50
Show Gist options
  • Save magnusnordlander/c8682fda2e15b813e5308624877cce59 to your computer and use it in GitHub Desktop.
Save magnusnordlander/c8682fda2e15b813e5308624877cce59 to your computer and use it in GitHub Desktop.
Vanmoof SA5 Certificate exporter

Requirements

  • node
  • @noble/ed25519
  • axios

How to use

  1. Update the last line of keypair-generator.js to use your Vanmoof account details.
  2. Run node keypair-generator.js > bikecredentials.txt
  3. Verify that bikecredentials.txt contains a private key ("Privkey"), and both a certificate and an ECU Serial for each SA5 bike in your account.
  4. Verify that the certificate(s) in bikecredentials.txt has an expiry 10 years in the future (previously this was 7 days)
  5. Save bikecredentials.txt somewhere safe. These files contain everything needed to communicate with your bike(s).
/*
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
const axios = require('axios');
const getCert = (async (email, password) => {
const ed = await import("@noble/ed25519");
const privKey = ed.utils.randomPrivateKey(); // Secure random private key
const pubKey = await ed.getPublicKeyAsync(privKey);
console.log("Privkey = " + Buffer.from(privKey).toString('base64'));
console.log("Pubkey = " + Buffer.from(pubKey).toString('base64'));
console.log("");
try {
const authResponse = await axios.post('https://my.vanmoof.com/api/v8/authenticate', null, {
headers: {
'Authorization': "Basic "+Buffer.from(email+':'+password).toString('base64'),
'Api-Key': "fcb38d47-f14b-30cf-843b-26283f6a5819",
'User-Agent': "VanMoof/20 CFNetwork/1404.0.5 Darwin/22.3.0",
}
});
const authToken = authResponse.data.token;
const appTokenResponse = await axios.get('https://api.vanmoof-api.com/v8/getApplicationToken', {
headers: {
'Authorization': "Bearer "+authToken,
'Api-Key': "fcb38d47-f14b-30cf-843b-26283f6a5819",
'User-Agent': "VanMoof/20 CFNetwork/1404.0.5 Darwin/22.3.0",
}
});
const appToken = appTokenResponse.data.token;
const bikeDataResponse = await axios.get('https://my.vanmoof.com/api/v8/getCustomerData?includeBikeDetails', {
headers: {
'Authorization': "Bearer "+authToken,
'Api-Key': "fcb38d47-f14b-30cf-843b-26283f6a5819",
'User-Agent': "VanMoof/20 CFNetwork/1404.0.5 Darwin/22.3.0",
}
});
for (const bikeIdx in bikeDataResponse.data.data.bikes) {
const bike = bikeDataResponse.data.data.bikes[bikeIdx];
console.log("Bike "+bike.name);
console.log("Bike ID: "+ bike.bikeId);
console.log("Frame number: "+ bike.frameNumber);
console.log("Frame serial: "+ bike.frameSerial);
if (bike.bleProfile === 'ELECTRIFIED_2022') {
console.log("Bike is an SA5");
console.log("ECU Serial: "+ bike.mainEcuSerial);
const certificateResponse = await axios.post('https://bikeapi.production.vanmoof.cloud/bikes/'+bike.bikeId+'/create_certificate', {
'public_key': Buffer.from(pubKey).toString('base64'),
}, {
headers: {
'Authorization': "Bearer "+appToken,
'User-Agent': "VanMoof/20 CFNetwork/1404.0.5 Darwin/22.3.0",
}
});
console.log("Certificate below:")
console.log("-----------");
console.log(certificateResponse.data);
console.log("-----------");
} else {
console.log("Not an SA5.")
}
}
console.log("");
console.log("");
} catch (error) {
console.log(error);
}
});
getCert("YOUR_VANMOOF_EMAIL", 'YOUR_VANMOOF_PASSWORD');
@KurgerBing281
Copy link

@gregmcd The python script worked! ✔️
Just had to install pynacl (which is still imported as nacl) using pip
(off-topic: so I guess npm is to nodejs, what pip is to python).
Thanks for your work.


@magnusnordlander Somewhat reassuring I'm not the only one being baffled by ssh-keygen sometimes. Thanks for the other tips.

Off-topic: I noted ed25519swift is made available by one 'pebble8888' - made me remember my old Pebble watch 💔, and the subsequent Rebble-servers that replaced the offline official Pebble servers after fitbit killed the Pebble brand. Should the same thing happen to VanMoof, I hope some community will provide a similar service.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment