Rooting the Cozytouch (aka Kizbox Mini) – Part 2

The OpenOCD Option

OpenOCD (on chip debugger) is not very intuitive, and it’s the kind of tool that if you’ve not used it for a while.. you have to restart from scratch!

It comes with a collection of supported devices, but out of the box, the AT91SAM9G25 is not one of those. Thanks to the author of this blog, he published a configuration file for our chip and OpenOCD: https://jopee.wordpress.com/6lowpan-gateway/

Here is the file, all credits goes to the author of the blog.

################################
# Target:    Atmel AT91SAM9G25
######################################
if { [info exists CHIPNAME] } {
set AT91_CHIPNAME $CHIPNAME
} else {
set AT91_CHIPNAME at91sam9g25
}
source [find target/at91sam9.cfg]
# Set fallback clock to 1/6 of worst-case clock speed (which would be the 32.768 kHz slow clock).
adapter_khz 5
# Establish internal SRAM memory work areas that are important to pre-bootstrap loaders, etc.  The
# AT91SAM9G25 has one 32kByte SRAM area, starting at 0x00300000.
$_TARGETNAME configure -work-area-phys 0x00300000 -work-area-size 0x8000 -work-area-backup 1
set _FLASHTYPE nandflash_cs3
# Set reset type.  Note that the AT91SAM9G20-EK board has the trst signal disconnected.  Therefore
# the reset needs to be configured for “srst_only”.  If for some reason, a zero-ohm jumper is
# added to the board to connect the trst signal, then this parameter may need to be changed.
reset_config srst_only
adapter_nsrst_delay 200
jtag_ntrst_delay 200
# If you don’t want to execute built-in boot rom code (and there are good reasons at times not to do that) in the
# AT91SAM9 family, the microcontroller is a lump on a log without initialization.  Because this family has
# some powerful features, we want to have a special function that handles “reset init”.  To do this we declare
# an event handler where these special activities can take place.
scan_chain
$_TARGETNAME configure -event reset-init {at91sam9g25_reset_init}
$_TARGETNAME configure -event reset-start {at91sam9g25_reset_start}
# NandFlash configuration and definition
nand device nandflash_cs3 at91sam9 $_TARGETNAME 0x40000000 0xffffe014
at91sam9 cle 0 22
at91sam9 ale 0 21
at91sam9 rdy_busy 0 0xfffff800 5
at91sam9 ce 0 0xfffffA00 4
proc read_register {register} {
        set result “”
        mem2array result 32 $register 1
        return $result(0)
}
proc at91sam9g25_reset_start { } {
# Make sure that the the jtag is running slow, since there are a number of different ways the board
# can be configured coming into this state that can cause communication problems with the jtag
# adapter.  Also since this call can be made following a “reset init” where fast memory accesses
# are enabled, need to temporarily shut this down so that the RSTC_MR register can be written at slower
# jtag speed without causing GDB keep alive problem.
arm7_9 fast_memory_access disable
adapter_khz 2                   ;# Slow-speed oscillator enabled at reset, so run jtag speed slow.
halt                            ;# Make sure processor is halted, or error will result in following steps.
wait_halt 10000
#mww 0xfffffe08 0xa5000001       ;# RSTC_MR : enable user reset.
#mww 0xFFFFFE00 0xA500000B ;# Reset CPU
}
proc at91sam9g25_reset_init { } {
mww 0xfffffe44 0x00008000 ;# WDT_MR : disable watchdog.
# Enable the main 12 MHz oscillator in CKGR_MOR register.
# Wait for MOSCS in PMC_SR to assert indicating oscillator is again stable after change to CKGR_MOR.
mww 0xfffffc20 0x01370F01
while { [expr [read_register 0xfffffc68] & 0x01] != 1 } { sleep 1 }
echo 1
## PMC Clock generator PLLA register
# DIVA = 3
# PLLACOUNT = 0x3F
# MULA = 0xc7
# OUTA = 0
# PLLA Frequency = 12 MHz / DIVA * (MULA + 1) = 800MHz
mww 0xfffffc28 0x20C73f03
while { [expr [read_register 0xfffffc68] & 0x02] != 2 } { sleep 1 }
echo 2
        mww 0xfffffc30 0x00001300
        while { [expr [read_register 0xfffffc68] & 0x08] != 8 } { sleep 1 }
echo 3
# Set master system clock prescaler divide by 6 and processor clock divide by 2 in PMC_MCKR.
# Wait for MCKRDY signal from PMC_SR to assert.
# System Clock = 800 MHz / 6 = 133MHz
# CPU Clock = 800 MHz / 2 = 400 MHz
#
mww 0xfffffc30 0x00001302
while { [expr [read_register 0xfffffc68] & 0x08] != 8 } { sleep 1 }
echo 4
# Switch over to adaptive clocking.
adapter_khz 100000
# Enable faster DCC downloads and memory accesses.
arm7_9 dcc_downloads enable
arm7_9 fast_memory_access enable
### Configure NAND
#
mww 0xFFFFFA04 0xffffffff
mww 0xfffffa74 0x3fef
mww 0xfffffc10 0x00000008
mww 0xfffffa00 0x00000030
mww 0xfffffa10 0x00000010
mww 0xfffffa30 0x00000010
mww 0xFFFFEA30 0x00020002
mww 0xFFFFEA34 0x04040404
mww 0xFFFFEA38 0x00070007
mww 0xFFFFEA3c 0x00030003
# configure NAND PMECC
### DRAM Setup
#
#
#
# CCFG_EBICSA
# Configure DRAM on EBI CS 1
# Set low drive strength
mww 0xFFFFDF20 0x0102000A
        # 0. Enable DDR2 Clock
        mww 0xfffffc00 0x4
# DDRSDRC_HS
# Disable Anticipated read access
mww 0xffffe82c 0x04
# DDRSDRC_MD
# 16-bit databus
# DDR-2
# According to the datasheet this value SHOULD be 0x14 for 16-bit DDR2!
mww 0xffffe820 0x16
# DDRSDRC_CR
#
# 10 col bits
# 13 row bits
# CAS 3
mww 0xffffe808 0x100039
## DDRSDRC_TPR0
#
mww 0xffffe80c 0x21222226
## DDRSDRC_TPR1
#
mww 0xffffe810 0x2c81312
## DDRSDRC_TPR2
#
mww 0xffffe814 0x1372
## DDRSDRC_MR
mww 0xffffe800 0x01 ;# NOP
mww 0x20000000 0x00
sleep 1
mww 0xffffe800 0x01 ;# NOP
mww 0x20000000 0x00
mww 0xffffe800 0x02 ;# All banks pre-charge
mww 0x20000000 0x00
mww 0xffffe800 0x05 ;# Extended load mode register
mww 0x24000000 0x00
mww 0xffffe800 0x05 ;# Extended load mode register
mww 0x26000000 0x00
mww 0xffffe800 0x05 ;# Extended load mode register
mww 0x22000000 0x00
## DDRSDRC_CR
mww 0xffffe808 0x1000b9
# Reset DLL
mww 0xffffe800 0x03 ;# LMR
mww 0x20000000 0x00
mww 0xffffe800 0x02 ;# All banks pre-charge
mww 0x20000000 0x00
mww 0xffffe800 0x04 ;# Auto refresh
mww 0x20000000 0x00
mww 0xffffe800 0x04 ;# Auto refresh
mww 0x20000000 0x00
        # DDRSDRC_CR
        #
        # 10 col bits
        # 13 row bits
        # CAS 3
        mww 0xffffe808 0x100039
       mww 0xffffe800 0x03 ;# LMR
        mww 0x20000000 0x00
        mww 0xffffe808 0x100739
       mww 0xffffe800 0x05 ;# eLMR
        mww 0x22000000 0x00
        mww 0xffffe808 0x100039
       mww 0xffffe800 0x05 ;# eLMR
        mww 0x26000000 0x00
       mww 0xffffe800 0x00 ;# Normal
        mww 0x20000000 0x00
## DDRSDRC_RTR
mww 0xffffe804 0x411
mww 0xffffe82c 0x04
sleep 1
mww 0x20000000 0xdeadbeef
mdw 0x20000000
nand probe 0
}

Save this file in /usr/share/openocd/scripts/board/at91sam9g25.cfg

Now, you need to create an openocd.cfg file, describing your setup, which is mainly:

  • your probe
  • the board
  • your own routines

I’ll not go through all the steps to create the file – it took me a while! Find below the content of the file I used.

# Use JLink interface (/usr/share/openocd/scripts/interface/jlink.cfg)
source [find interface/jlink.cfg]

# JLink requires a speed to be used by the probe. 0 means adaptive.
adapter_khz 0

# JLink also requires to specify the transport type. Here we use JTAG
transport select jtag

# Load the board config file for the at91
source [find board/at91sam9g25.cfg]

# My own initialization routine. Reset the device, and configure the NAND bank for access.
proc reset_ben {} {
reset halt
adapter_khz 0

# Configure NAND

mww 0xFFFFFA04 0xffffffff
mww 0xfffffa74 0x3fef
mww 0xfffffc10 0x00000008
mww 0xfffffa00 0x00000030
mww 0xfffffa10 0x00000010
mww 0xfffffa30 0x00000010
mww 0xFFFFEA30 0x00020002
mww 0xFFFFEA34 0x04040404
mww 0xFFFFEA38 0x00070007
mww 0xFFFFEA3c 0x00030003

# Configure NAND PMECC
### DRAM Setup

# CCFG_EBICSA
# Configure DRAM on EBI CS 1
# Set low drive strength
mww 0xFFFFDF20 0x0102000A
# 0. Enable DDR2 Clock
mww 0xfffffc00 0x4

# DDRSDRC_HS
# Disable Anticipated read access
mww 0xffffe82c 0x04

# DDRSDRC_MD
# 16-bit databus
# DDR-2
# According to the datasheet this value SHOULD be 0x14 for 16-bit DDR2!
mww 0xffffe820 0x16
# DDRSDRC_CR
#
# 10 col bits
# 13 row bits
# CAS 3
mww 0xffffe808 0x100039
# DDRSDRC_TPR0
mww 0xffffe80c 0x21222226
# DDRSDRC_TPR1
mww 0xffffe810 0x2c81312
# DDRSDRC_TPR2
mww 0xffffe814 0x1372
# DDRSDRC_MR
mww 0xffffe800 0x01 ;# NOP
mww 0x20000000 0x00
sleep 1
mww 0xffffe800 0x01 ;# NOP
mww 0x20000000 0x00
mww 0xffffe800 0x02 ;# All banks pre-charge
mww 0x20000000 0x00
mww 0xffffe800 0x05 ;# Extended load mode register
mww 0x24000000 0x00
mww 0xffffe800 0x05 ;# Extended load mode register
mww 0x26000000 0x00
mww 0xffffe800 0x05 ;# Extended load mode register
mww 0x22000000 0x00
# DDRSDRC_CR
mww 0xffffe808 0x1000b9
# Reset DLL
mww 0xffffe800 0x03 ;# LMR
mww 0x20000000 0x00
mww 0xffffe800 0x02 ;# All banks pre-charge
mww 0x20000000 0x00
mww 0xffffe800 0x04 ;# Auto refresh
mww 0x20000000 0x00
mww 0xffffe800 0x04 ;# Auto refresh
mww 0x20000000 0x00

# DDRSDRC_CR
#
# 10 col bits
# 13 row bits
# CAS 3
mww 0xffffe808 0x100039
mww 0xffffe800 0x03 ;# LMR
mww 0x20000000 0x00
mww 0xffffe808 0x100739
mww 0xffffe800 0x05 ;# eLMR
mww 0x22000000 0x00
mww 0xffffe808 0x100039
mww 0xffffe800 0x05 ;# eLMR
mww 0x26000000 0x00
mww 0xffffe800 0x00 ;# Normal
mww 0x20000000 0x00

# DDRSDRC_RTR
mww 0xffffe804 0x411
mww 0xffffe82c 0x04
sleep 1
mww 0x20000000 0xdeadbeef
mdw 0x20000000
nand probe 0
}

You can now simply start openocd. If everything goes right, you should see something like this:

Starting OpenOCD

You can now interact with OpenOCD, telnetting to the local port 4444, and start with my routine “reset_ben”:

Interacting with OpenOCD

The NAND flash device has been found and identified correctly. Great! We can now start dumping the NAND. For memory, here is the list of available NAND commands:

nand
      NAND flash command group (command valid any time)
  nand check_bad_blocks bank_id [offset length]
        check all or part of NAND flash device for bad blocks
  nand device bank_id driver target [driver_options ...]
        defines a new NAND bank (configuration command)
  nand drivers
        lists available NAND drivers (command valid any time)
  nand dump bank_id filename offset length ['oob_raw'|'oob_only']
        dump from NAND flash device
  nand erase bank_id [offset length]
        erase all or subset of blocks on NAND flash device
  nand info [banknum | first_bank_num last_bank_num]
        print info about one or more NAND flash devices
  nand init
        initialize NAND devices (configuration command)
  nand list
        list configured NAND flash devices
  nand probe bank_id
        identify NAND flash device
  nand raw_access bank_id ['enable'|'disable']
        raw access to NAND flash device
  nand verify bank_id filename offset
            ['oob_raw'|'oob_only'|'oob_softecc'|'oob_softecc_kw']
        verify NAND flash device
  nand write bank_id filename offset
            ['oob_raw'|'oob_only'|'oob_softecc'|'oob_softecc_kw']
        write to NAND flash device
List available banks

Let’s start dumping! Here, it’s a bit up to you, but I prefer dumping with small lots of 1MB, which are 8 blocks of 131072. There are 1024 blocks, dumping with 1MB, it makes 128 dumps.

Dump a 1MB block

Ok… I’m not going to dump 1 by 1 by hand… so a quick python script to generate the dump instructions (there was probably a way to do it in OpenOCD…):

#!/usr/bin/python3

# 1MB Bloc size
dumpSize = 0x100000

# Number of blocks
blocks = 128

s = "nand dump 0 /data/cozytouch/2020_11_08-nand-dump/{0:04d}.bin 0x{1:x} 0x{2:x}"

for i in range(blocks):
    print(s.format(i,i*dumpSize,dumpSize))

Copy paste in your OpenOCD telnet window… and go grab a coffee ☕️ !

P.S. : I found that after dumping 2 blocks, the board became unstable. Quick [and dirty – but who cares] workaround: reset the board every 2 dumps:

    if i % 2 == 0:
        print("reset_ben")

–> Go to Part 3

Rooting the Cozytouch (aka Kizbox Mini) – Part 3

Analyzing the dump

Now that you have dumped the 128 blocks of 1MB – its time to re assemble it and start analyzing it.

cat *.bin > nand.bin

Favorite tool to start with… binwalk ! The output is not very verbose, but indicates that the NAND flash uses the UBI filesystem – which is common for NAND on embedded devices.

If we want to be able to work with the NAND image – and list and mount partition, one option is to emulate on our linux an MTD device. The various parameter describe flash size, block size, etc.

modprobe nandsim first_id_byte=0xc2 second_id_byte=0xf1 third_id_byte=0x80 fourth_id_byte=0x95

The parameters values are directly taken from the Macronix documentation – to emulate the exact NAND device:

Macronix NAND flash documentation

Emulating a virtual NAND identical to the device

Now that the virtual NAND flash is created, just copy the content of the dump on it:

Flashing the virtual NAND

Enable the UBIFS, indicate that it’s on mtd0, and the block size is 2048:

Enabling UBIFS

And – tadaaaa:

List of UBI volumes

The full filesystem is composed of 11 volumes:

Volume IDSize (bytes)Name
025 197dtb
125 197dtb-spare
24 893 365kboot
34 893 365kboot-spare
48 192security
58 192security-spare
63 174 400persistent
726 411 008root
823 871 488apps
926 284 032rootB
10126 976persistent-spare

The device has some redundancy/failover in place to prevent a corrupted storage/partition and seems to have self-restore facility (-spare volumes).

You can find the filesystem / data type using binwalk on each UBI volume, for example:

DTB (device tree binding) is used by the kernel to describe the hardware, and determine modules/drivers to be loaded. We’ll not be touching at it.

There is next KBOOT – which is the device specific bootloader.

Finally, we are interested in the following volumes (not mentioning the spare one):

– security : SquashFS
– persistent : UbiFS
– root : UbiFS
– apps : UbiFS

UbiFS partitions can be mounted straightforward – whereas the SquashFS needs to be extracted.

Now, mount each filesystem:

Mounting partitions
Extracting the SquashFS image

At this stage, you can start exploring the device file systems, and understand the logic. Looking at /etc/fstab from the root partition indicates that apps and persistent partitions are mounted in /apps and /persistent respectively:

/etc/fstab

By default, all the ports are closed on the device. What we want to do is be able to connect to the device first. What about enabling SSH ? read it in the next part !

–> Go to Part 4

Rooting the Cozytouch (aka Kizbox Mini) – Part 1

The Cozytouch is an home automation gateway from the French manufacturer Atlantique/Thermor and also sold under the Sauter brand, depending on selling channel. They’re all identical, both from hardware and software perspective.

The Cozytouch brings the connectivity to various appliances (water boiler, heater…) and the Atlantic cloud (bridge). It allows users to control their appliance from a mobile app available on iOS/Android. Appliances and the bridge are communicating over a closed-source wireless protocol called IOHomeControl operating in the 868mhz band.

The bridge itself, the cloud and the protocols are closed-source and undocumented. The features and data accessible are also very limited. As such, in order to better integrate my water boiler in my home automation, I decided to root the device. 😈

Another option could be hacking the wireless protocol using an RTL/SDR, but if the crypto is well done, it is likely to be a very long way with little results.

Dissassembly & PCB analysis.

The board is manufacturer/designed by Overkiz, a subsidiary of French manufacturer Somfy.

The board design is pretty simple. It is based on 2 SoC which seems to communicate through a simple UART.

The first SoC, an Atmel AT91SAM9G25, is running the Linux system and in charge of edge-computing feature and communication with the cloud (no wifi – only ethernet). It is surrounded by a 1GB Nand Flash (MX30LF1G18AC) and a 1GB DDR2 RAM module (MT47H64M16NF-25E).

The second SoC, an STMicro ST32, is used exclusively for the radio communication (SDR). It will be found later once the OS is rooted, that this SOC can operate various protocols in various wavelength.

The pure software angle did not provide any way to get in the device. No open port, no upgrade protocol allowing MiTM, encryption made properly… No alternative: go the hard way!

Interacting with the board

From a hardware perspective, no pins, no port, but soldering points! Long story short, here are the useful pins (I will not detail the methodology to identify useful pins and how to use tools, which includes logic analyser, serial-TTL, jtagulator or similar ones, arduino… there are tons of tutorials on the web for that!).

The 2 useful groups of connectors:

  • Serial console
  • JTAG port

The serial console, as-is, is useless. The firmware has been hardened, and the serial console is disabled in the boot-loader. Upon restart, it is just possible to access the version of bootloader:

Boot trace

We’ll be using the serial console, later.

Using the JTAG port, my first objective was to dump the flash and access the firmware. I did it using 2 techniques:

  • OpenOCD
  • Booting a custom uBoot, dumping the flash on a TFTP server

As a JTAG probe, I went (and recommend) to go with a JLink-like (Aliexpress is your friend!). Another perfect alternative which I’ve used for long is the Raspberry PI GPIO ! It might be slower than a dedicated hardware probe, but definitely working and a Swiss army knife.

–> Go to Part 2

Monitoring an home automation installation properly

Having home automation is great. Having it reliable and available 24/7 is even better! Especially when you use smart locks, fire sensors alarms and so on.

In this article, I’ll detail how I setup the monitoring of my Home Automation infrastructure using Zabbix. The perfect monitoring is a mix of detection and also alerting.

First of all – you need to identify what are your different components, and what do you want to monitor ? Which layers ?

Bottom-up – it starts with the hardware:

  • Switches
  • Routers
  • Servers
  • Appliances
  • Gateways (KNX-IP…)

Next, system:

  • Virtual Machines
    • Operating System
  • Containers (Docker)

Software:

  • openHAB

Components:

  • openHAB binding states

To begin with, I decided to startup with a “Dockerized” Zabbix environment. My stack is very simple:

---
version: "2"
networks:
  zabbix-internal:
    driver: bridge
  web:
    external:
      name: web
services:
  zabbix-mysql:
    image: mysql
    container_name: zabbix-mysql
    networks:
      zabbix-internal:
    environment:
      MYSQL_ROOT_PASSWORD: *********
    volumes:
      - "/srv/iscsi/dockers/zabbix/mysql:/var/lib/mysql"
    restart: unless-stopped
    labels:
      - "traefik.enable=false"
  zabbix-server:
    # Documentation: https://hub.docker.com/r/zabbix/zabbix-server-mysql
    image: zabbix/zabbix-server-mysql
    container_name: zabbix-server
    restart: unless-stopped
    networks:
      zabbix-internal:
    ports:
      - "10051:10051"
    environment:
      TZ: "Europe/Paris"
      DB_SERVER_HOST: "zabbix-mysql"
      MYSQL_USER: "root"
      MYSQL_PASSWORD: "**********"
      ZBX_STARTPINGERS: 5
      ZBX_JAVAGATEWAY: "zabbix-java-gateway"
      ZBX_JAVAGATEWAYPORT: 10052
      ZBX_JAVAGATEWAY_ENABLE: "true"
      ZBX_STARTJAVAPOLLERS: 5
    labels:
      - "traefik.enable=false"
  zabbix-java-gateway:
    image: zabbix/zabbix-java-gateway
    container_name: zabbix-java-gateway
    restart: unless-stopped
    networks:
      zabbix-internal:
    environment:
      TZ: "Europe/Paris"
    labels:
      - "traefik.enable=false"
  zabbix-frontend:
    image: zabbix/zabbix-web-nginx-mysql
    container_name: zabbix-frontend
    restart: unless-stopped
    networks:
      zabbix-internal:
      web:
    environment:
      TZ: "Europe/Paris"
      PHP_TZ: "Europe/Paris"
      DB_SERVER_HOST: "zabbix-mysql"
      MYSQL_USER: "zabbix-frontend"
      MYSQL_PASSWORD: "*****************"
      ZBX_SERVER_HOST: "zabbix-server"
      ZBX_SERVER_NAME: "Ben"
  

You’ll notice that in addition to a “basic” Zabbix stack, I added Zabbix Java Gateway, to be able to monitor openHAB JVM properly.

The monitoring of hardware is pretty easy and standard: SNMP, or even better, Zabbix Agent ! Most hardware support SNMP (I mean – decent hardware), and most operating system, whatever architecture, avec Zabbix agent packaged in standard package manager they come with. Devices that I monitor using SNMP & Zabbix agents:

  • Router (Ubiquiti EdgeRouter)
  • Switches (Ubiquiti EdgeSwitch)
  • Wireless Access Points (Ubiquiti Unifi NanoHD, InWall HD)
  • Linux hosts (VM and physical – including Raspberry, OrangePi…)
  • ESXi hypervisor
  • Synology NAS (storage + CCTV)

There are devices that you want to monitor but unfortunately don’t provide SNMP. This is for example the case of Apple TV, Sonos devices, and Tasmota devices, which I widely use for their cost and efficiency in my installation. For those device, you have multiple choice:

  • monitor that they are alive using traditional ICMP ping
  • monitor that the services are up (HTTP for example)

I found that ICMP was sufficient for Apple TV, Sonos and Tasmota devices.

Your monitoring should be driven by your “threats”: what are you scared about ?

In my case, I am scared of being locked down out of my apartment. So I need to be sure that openHAB is working well (Java app -> monitor the JVM), ensure that openHAB is well connected to the various bridges/gateways (bindings), and also ensures that some devices are still reporting alive (zigbee battery devices).

I will not detail the basic steps of creating a HOST and configuring a Zabbix environment, Google is full of that ! I will only focus on specific aspects of openHAB.

We will be using the openHAB REST API to monitor the inside of openHAB and bindings:


Then:

Once you have created your item within a host, just create a trigger:

Integrating Daikin Madoka (BRC1H) in openHAB

Daikin Madoka, also known as BRC1H, are new generation of home air-conditioning wired thermostat, that also allow control from mobile phone through Daikin “Madoka Assistant” application.

I got 3 Madoka thermostat installed in my new flat and having them connected to the rest of my Home Automation infrastructure was not optional!

Several options :

  • Buy a Daikin KNX gateway (3 necessary – one on each internal unit)
  • Develop a component that could take benefit of BRC1H/Madoka thermostats.

The KNX gateways are expensive (~300€ / unit), and not sure about their good integration/availability – could find very limited information on it – and as I’m still a geek with some technical skills, I decided to go with option #2 – develop a component interacting with Madoka thermostats!

The reverse engineering of the protocol used by the mobile app and the Madoka thermostat can be found on my github: https://github.com/blafois/Daikin-Madoka-BRC1H-BLE-Reverse

In a future article, I’ll detail the steps to setup a BLE Peripheral and BLE Central using Bleno and Noble (JS).