Commit e520505e authored by Loïck Bonniot's avatar Loïck Bonniot

[c][t][p] Integrate TTP in signature workflow

This huge commit adds the following features:

- Change default timeout to 5 sec (to be updated soon)
- Don't send TTP information in contract file, but during start signal
- Platform: load and save TTP data from a local file
  New flags: --addr and --ttps
- Platform ttp command: automatically save ttp data in the local file
  (addr and certificate hash are the only values needed by the platform
  and the clients)
- Update integration tests to integrate TTP
parent 7db9f833
Pipeline #1952 passed with stage
......@@ -51,8 +51,13 @@ type Context struct {
ContractDocumentHash []byte `protobuf:"bytes,5,opt,name=contractDocumentHash,proto3" json:"contractDocumentHash,omitempty"`
// / The unique signature attemp ID, as provided by the platform during the ready signal
SignatureUUID string `protobuf:"bytes,6,opt,name=signatureUUID" json:"signatureUUID,omitempty"`
// / The TTP address provided by the platform, to use in case of issue during signature.
// / Empty if no TTP used.
TtpAddrPort string `protobuf:"bytes,7,opt,name=ttpAddrPort" json:"ttpAddrPort,omitempty"`
// / The TTP certificate SHA-512 hash
TtpHash []byte `protobuf:"bytes,8,opt,name=ttpHash,proto3" json:"ttpHash,omitempty"`
// / The signed metadata seal, as provided by the platform during the ready signal
Seal []byte `protobuf:"bytes,7,opt,name=seal,proto3" json:"seal,omitempty"`
Seal []byte `protobuf:"bytes,10,opt,name=seal,proto3" json:"seal,omitempty"`
func (m *Context) Reset() { *m = Context{} }
......@@ -262,27 +267,31 @@ var _Client_serviceDesc = grpc.ServiceDesc{
var fileDescriptor0 = []byte{
// 348 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x91, 0xcf, 0x4e, 0xea, 0x40,
0x14, 0xc6, 0xe9, 0xe5, 0x4f, 0xe1, 0xdc, 0xc2, 0x25, 0x93, 0x6b, 0x32, 0x41, 0x49, 0x48, 0xe3,
0x82, 0x55, 0x49, 0x70, 0xe9, 0x12, 0x4c, 0x30, 0x6e, 0x4c, 0x94, 0x07, 0x18, 0xa7, 0x07, 0x9d,
0xa4, 0x74, 0xea, 0xcc, 0x60, 0xe0, 0x35, 0x7c, 0x02, 0x1f, 0xd5, 0xe9, 0xa9, 0x45, 0x16, 0x26,
0x6e, 0x9a, 0x9e, 0x6f, 0xbe, 0xf3, 0xf5, 0xf7, 0x75, 0xe0, 0x3c, 0xdd, 0x58, 0x3b, 0x2b, 0x1f,
0x72, 0x26, 0x0a, 0x35, 0x93, 0x99, 0xc2, 0xdc, 0x25, 0x85, 0xd1, 0x4e, 0xb3, 0xa6, 0x57, 0x46,
0xe3, 0xa3, 0xa3, 0x20, 0x47, 0x91, 0x09, 0xb7, 0xd1, 0x66, 0x5b, 0x79, 0xe2, 0x8f, 0x00, 0xc2,
0x85, 0xce, 0x1d, 0xee, 0x1d, 0xe3, 0x30, 0x34, 0x28, 0x55, 0x51, 0x46, 0xdc, 0xe1, 0x61, 0x25,
0xec, 0x0b, 0x0f, 0x26, 0xc1, 0x34, 0x62, 0x67, 0xd0, 0xb7, 0x98, 0xa7, 0x68, 0x6a, 0xf9, 0x0f,
0xc9, 0x43, 0xe8, 0x5a, 0x7c, 0xdd, 0x61, 0x2e, 0x91, 0x37, 0x27, 0xcd, 0x69, 0x9f, 0xfd, 0x83,
0xd0, 0xaa, 0xe7, 0x1c, 0x8d, 0xe5, 0x2d, 0x2f, 0x44, 0xec, 0x02, 0xfe, 0x4b, 0x1f, 0x6f, 0x84,
0x74, 0x4b, 0x2d, 0x77, 0x5b, 0x1f, 0x4d, 0x01, 0xed, 0x63, 0xae, 0xb7, 0x0b, 0xb7, 0x33, 0xb8,
0x5e, 0xdf, 0x2e, 0x79, 0xc7, 0xcb, 0x3d, 0x16, 0x41, 0xcb, 0xa2, 0xc8, 0x78, 0x58, 0x9a, 0xe2,
0x15, 0x84, 0xf7, 0x46, 0x6f, 0x95, 0x45, 0x36, 0x86, 0x50, 0x56, 0xb0, 0x04, 0xf6, 0x77, 0x1e,
0x25, 0xbe, 0x53, 0x52, 0x17, 0xe8, 0x43, 0x5b, 0x79, 0xca, 0x3d, 0xe1, 0x11, 0x4c, 0x21, 0x0e,
0x99, 0x16, 0xa9, 0xa7, 0x2b, 0x93, 0xae, 0xa1, 0xf7, 0x50, 0x7f, 0xee, 0xb7, 0xac, 0x93, 0x65,
0x2a, 0x1b, 0x73, 0x68, 0xaf, 0x30, 0xcb, 0x74, 0x79, 0xf2, 0xe6, 0x0b, 0x2a, 0x9d, 0xd3, 0x62,
0x6f, 0xfe, 0x1e, 0x40, 0x67, 0x41, 0x3f, 0x9e, 0x25, 0x10, 0x3d, 0x1a, 0x14, 0xae, 0x06, 0xae,
0x32, 0xbf, 0xa6, 0xd1, 0x80, 0xa6, 0x1b, 0x63, 0xb4, 0x59, 0xe8, 0x14, 0xe3, 0x06, 0x9b, 0xc3,
0x80, 0xfc, 0xdf, 0x58, 0x95, 0xe7, 0x38, 0xff, 0xb0, 0x73, 0x09, 0xdd, 0xa5, 0xb2, 0x52, 0x7b,
0x08, 0x06, 0x74, 0x4a, 0x5c, 0xa3, 0x93, 0xf7, 0xb8, 0xf1, 0xd4, 0xa1, 0xfb, 0xbd, 0xfa, 0x0c,
0x00, 0x00, 0xff, 0xff, 0x64, 0x27, 0xd3, 0xc2, 0x22, 0x02, 0x00, 0x00,
// 404 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x52, 0xdd, 0xca, 0xda, 0x40,
0x10, 0xf5, 0x3f, 0x3a, 0x46, 0x29, 0x8b, 0x17, 0x21, 0xa5, 0x60, 0x83, 0x14, 0xe9, 0x45, 0x04,
0xfb, 0x04, 0x45, 0x0b, 0x96, 0x52, 0x90, 0xb4, 0x3e, 0xc0, 0x76, 0x33, 0xb6, 0x0b, 0x31, 0x9b,
0xee, 0xae, 0x45, 0x5f, 0xa3, 0xaf, 0xd3, 0x97, 0xeb, 0x66, 0x62, 0xfc, 0x94, 0xcf, 0x8b, 0xef,
0x26, 0xec, 0x39, 0x73, 0x76, 0xce, 0xec, 0xc9, 0xc0, 0xeb, 0x74, 0x6f, 0xcc, 0xa2, 0xfc, 0x88,
0x05, 0x2f, 0xe4, 0x42, 0x64, 0x12, 0x73, 0x1b, 0x17, 0x5a, 0x59, 0xc5, 0xda, 0x8e, 0x09, 0xdf,
0x5c, 0x15, 0x05, 0x29, 0x8a, 0x8c, 0xdb, 0xbd, 0xd2, 0x87, 0x4a, 0x13, 0xfd, 0x6b, 0x81, 0xb7,
0x52, 0xb9, 0xc5, 0x93, 0x65, 0xef, 0xe1, 0x95, 0x46, 0x21, 0x8b, 0xb2, 0xc5, 0x17, 0x3c, 0x6f,
0xb8, 0xf9, 0x15, 0x34, 0xa7, 0xcd, 0xb9, 0x9f, 0x3c, 0xe3, 0xd9, 0x0c, 0x46, 0x06, 0xf3, 0x14,
0x75, 0x2d, 0x6c, 0x91, 0xf0, 0x9e, 0x64, 0x21, 0xf4, 0x0d, 0xfe, 0x3e, 0x62, 0x2e, 0x30, 0x68,
0x4f, 0xdb, 0xf3, 0x51, 0x72, 0xc5, 0x2c, 0x00, 0xcf, 0xc8, 0x9f, 0x39, 0x6a, 0x13, 0x74, 0x5c,
0xc9, 0x4f, 0x6a, 0xc8, 0x96, 0x30, 0x11, 0x6e, 0x24, 0xcd, 0x85, 0x5d, 0x2b, 0x71, 0x3c, 0x38,
0x5b, 0xb2, 0xe8, 0x92, 0xc5, 0xc3, 0x1a, 0xcd, 0xe3, 0xae, 0x73, 0x7b, 0xd4, 0xb8, 0xdb, 0x7d,
0x5e, 0x07, 0x3d, 0x27, 0x1e, 0x24, 0xf7, 0x24, 0x9b, 0xc2, 0xd0, 0xda, 0xe2, 0x63, 0x9a, 0xea,
0xad, 0xd2, 0x36, 0xf0, 0x48, 0x73, 0x4b, 0x95, 0x53, 0x39, 0x48, 0x76, 0x7d, 0xb2, 0xab, 0x21,
0x63, 0xd0, 0x31, 0xc8, 0xb3, 0x00, 0x88, 0xa6, 0x73, 0xc4, 0xc1, 0xdb, 0x6a, 0x75, 0x90, 0x06,
0xd9, 0x3b, 0xf0, 0x44, 0x95, 0x23, 0x65, 0x36, 0x5c, 0xfa, 0xb1, 0x8b, 0x3b, 0xbe, 0x64, 0x9b,
0xd4, 0x45, 0x36, 0x81, 0xae, 0x74, 0x11, 0x9d, 0x28, 0xb0, 0x51, 0x52, 0x81, 0xd2, 0xb6, 0xe0,
0xe7, 0x4c, 0xf1, 0xd4, 0xe5, 0x44, 0xb6, 0x17, 0x18, 0x7d, 0x85, 0xc1, 0xb7, 0xfa, 0x0d, 0x2f,
0x36, 0xb9, 0x69, 0xd7, 0xba, 0x6f, 0xf7, 0x16, 0xba, 0x1b, 0xcc, 0x32, 0x55, 0x4a, 0xfe, 0xb8,
0xb0, 0xa5, 0xca, 0xa9, 0xd5, 0x20, 0xa9, 0xe1, 0xf2, 0x6f, 0x13, 0x7a, 0x2b, 0xda, 0x23, 0x16,
0x83, 0xff, 0x5d, 0x23, 0xb7, 0xf5, 0x23, 0x2b, 0xbb, 0x0b, 0x0a, 0xc7, 0x84, 0x3e, 0x69, 0xad,
0xf4, 0x4a, 0xa5, 0x18, 0x35, 0xdc, 0x9f, 0x1b, 0x93, 0xfe, 0x69, 0xe2, 0x4a, 0x73, 0xc5, 0x0f,
0xee, 0xcc, 0xa0, 0xbf, 0x96, 0x46, 0x28, 0x67, 0xcf, 0x80, 0xaa, 0x34, 0x60, 0x78, 0x73, 0x8e,
0x1a, 0x3f, 0x7a, 0xb4, 0xae, 0x1f, 0xfe, 0x07, 0x00, 0x00, 0xff, 0xff, 0x63, 0xf5, 0x69, 0x69,
0xf1, 0x02, 0x00, 0x00,
......@@ -30,8 +30,13 @@ message Context {
bytes contractDocumentHash = 5;
/// The unique signature attemp ID, as provided by the platform during the ready signal
string signatureUUID = 6;
/// The TTP address provided by the platform, to use in case of issue during signature.
/// Empty if no TTP used.
string ttpAddrPort = 7;
/// The TTP certificate SHA-512 hash
bytes ttpHash = 8;
/// The signed metadata seal, as provided by the platform during the ready signal
bytes seal = 7;
bytes seal = 10;
message Promise {
......@@ -25,6 +25,8 @@ func (m *SignatureManager) createContext(from, to uint32) (*cAPI.Context, error)
Signers: m.keyHash,
ContractDocumentHash: h,
SignatureUUID: m.uuid,
TtpAddrPort: m.ttpData.Addrport,
TtpHash: m.ttpData.Hash,
Seal: m.seal,
}, nil
......@@ -14,9 +14,9 @@ import (
dAPI "dfss/dfssd/api"
tAPI "dfss/dfsst/api"
// Sign performs all the message exchanges for the contract to be signed
......@@ -49,8 +49,10 @@ func (m *SignatureManager) Sign() error {
// Promess rounds
// Follow the sequence until there is no next occurence of me
round := 0
for m.currentIndex >= 0 {
round = round + 1
m.OnProgressUpdate(m.currentIndex, seqLen+1)
dAPI.DLog("starting round at index [" + fmt.Sprintf("%d", m.currentIndex) + "] with nextIndex=" + fmt.Sprintf("%d", nextIndex))
......@@ -132,7 +134,7 @@ func (m *SignatureManager) promiseRound(pendingSet, sendSet []common.SequenceCoo
return m.resolve()
case <-time.After(time.Minute):
case <-time.After(net.DefaultTimeout):
return m.resolve()
......@@ -235,16 +237,20 @@ func (m *SignatureManager) callForResolve() (*tAPI.TTPResponse, error) {
// resolve : calls for the resolution, and persists the contract if obtained.
func (m *SignatureManager) resolve() error {
if m.ttp == nil {
dAPI.DLog("unable to contact TTP")
return errors.New("No connection to TTP, aborting!")
dAPI.DLog("contacting TTP")
response, err := m.callForResolve()
if err != nil {
return err
if response.Abort {
dAPI.DLog("contacted TTP, received abort token")
return nil
dAPI.DLog("contacted TTP, received signed contract")
return ioutil.WriteFile(m.mail+"-"+m.contract.UUID+".proof", response.Contract, 0600)
......@@ -283,7 +289,7 @@ func stopIfNeeded(index int) {
if index == -1 && s == -1 || index+1 == s {
if index == -1 && s == -1 || index == s {
\ No newline at end of file
package sign
import (
......@@ -34,6 +32,7 @@ type SignatureManager struct {
platform pAPI.PlatformClient
platformConn *grpc.ClientConn
ttp tAPI.TTPClient
ttpData *pAPI.LaunchSignature_TTP
peersConn map[string]*grpc.ClientConn
peers map[string]*cAPI.ClientClient
hashToID map[string]uint32
......@@ -98,12 +97,6 @@ func NewSignatureManager(passphrase string, c *contract.JSON) (*SignatureManager
m.platform = pAPI.NewPlatformClient(connp)
m.platformConn = connp
// connect to the ttp
connt, err := m.connectToTTP(m.auth.Cert, m.auth.Key, m.auth.CA)
if err == nil {
m.ttp = tAPI.NewTTPClient(connt)
m.peersConn = make(map[string]*grpc.ClientConn)
m.peers = make(map[string]*cAPI.ClientClient)
for _, u := range c.Signers {
......@@ -119,17 +112,6 @@ func NewSignatureManager(passphrase string, c *contract.JSON) (*SignatureManager
return m, nil
// connectToTTP : tries to open a connection with the ttp specified in the contract.
// If there was no specified ttp, returns an error.
// Otherwise, returns the connection, or an error if something else occured.
func (m *SignatureManager) connectToTTP(cert *x509.Certificate, key *rsa.PrivateKey, ca *x509.Certificate) (*grpc.ClientConn, error) {
if m.contract.TTP == nil {
return nil, errors.New("No specified ttp in contract")
addrPort := m.contract.TTP.IP + ":" + string(m.contract.TTP.Port)
return net.Connect(addrPort, cert, key, ca, nil)
// ConnectToPeers tries to fetch the list of users for this contract, and tries to establish a connection to each peer.
func (m *SignatureManager) ConnectToPeers() error {
localIps, err := net.ExternalInterfaceAddr()
......@@ -299,6 +281,9 @@ func (m *SignatureManager) SendReadySign() (signatureUUID string, err error) {
// Connect to TTP, if any
err = m.connectToTTP(launch.Ttp)
m.sequence = launch.Sequence
m.uuid = launch.SignatureUuid
m.keyHash = launch.KeyHash
......@@ -307,6 +292,27 @@ func (m *SignatureManager) SendReadySign() (signatureUUID string, err error) {
// connectToTTP : tries to open a connection with the ttp specified in the contract.
func (m *SignatureManager) connectToTTP(ttp *pAPI.LaunchSignature_TTP) error {
if ttp == nil {
m.ttpData = &pAPI.LaunchSignature_TTP{
Addrport: "",
Hash: []byte{},
return nil
// TODO check that the connection spots missing TTP and returns an error quickly enough
conn, err := net.Connect(ttp.Addrport, m.auth.Cert, m.auth.Key, m.auth.CA, ttp.Hash)
if err != nil {
return err
m.ttpData = ttp
m.ttp = tAPI.NewTTPClient(conn)
return nil
// Initialize computes the values needed for the start of the signing
func (m *SignatureManager) Initialize() (int, error) {
myID, err := m.FindID()
......@@ -135,15 +135,16 @@ var _Demonstrator_serviceDesc = grpc.ServiceDesc{
var fileDescriptor0 = []byte{
// 159 bytes of a gzipped FileDescriptorProto
// 167 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x52, 0x4c, 0x49, 0x2b, 0x2e,
0xd6, 0x07, 0x11, 0x29, 0xfa, 0x89, 0x05, 0x99, 0xfa, 0x29, 0xa9, 0xb9, 0xf9, 0x79, 0xc5, 0x25,
0x45, 0x89, 0x25, 0xf9, 0x45, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xcc, 0x40, 0x71, 0x25,
0x4b, 0x2e, 0x66, 0x9f, 0xfc, 0x74, 0x21, 0x41, 0x2e, 0xce, 0x92, 0xcc, 0xdc, 0xd4, 0xe2, 0x92,
0xc4, 0xdc, 0x02, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x66, 0x21, 0x21, 0x2e, 0xae, 0xcc, 0x94, 0xd4,
0xbc, 0x92, 0xcc, 0xb4, 0xcc, 0xd4, 0x22, 0x09, 0x26, 0xa0, 0x18, 0xa7, 0x10, 0x37, 0x17, 0x73,
0x4e, 0x7e, 0xba, 0x04, 0x33, 0x88, 0xa3, 0xc4, 0xca, 0xc5, 0xec, 0x98, 0x9c, 0x6d, 0xa4, 0xcf,
0xc5, 0xe3, 0x82, 0x64, 0xb8, 0x90, 0x3c, 0x17, 0x7b, 0x70, 0x6a, 0x5e, 0x0a, 0xc8, 0x54, 0x0e,
0x3d, 0xa0, 0x15, 0x7a, 0x40, 0x96, 0x14, 0x84, 0x05, 0x54, 0xae, 0xc4, 0x90, 0xc4, 0x06, 0xb6,
0xde, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x16, 0xf4, 0xd0, 0x37, 0xa3, 0x00, 0x00, 0x00,
0x45, 0x89, 0x25, 0xf9, 0x45, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xcc, 0x40, 0x71, 0xa5,
0x50, 0x2e, 0x66, 0x9f, 0xfc, 0x74, 0x21, 0x19, 0x2e, 0xce, 0x92, 0xcc, 0xdc, 0xd4, 0xe2, 0x92,
0xc4, 0xdc, 0x02, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xe6, 0x20, 0x84, 0x80, 0x90, 0x1c, 0x17, 0x57,
0x66, 0x4a, 0x6a, 0x5e, 0x49, 0x66, 0x5a, 0x66, 0x6a, 0x91, 0x04, 0x13, 0x50, 0x9a, 0x33, 0x08,
0x49, 0x44, 0x48, 0x80, 0x8b, 0x39, 0x27, 0x3f, 0x5d, 0x82, 0x19, 0x2c, 0x01, 0x62, 0x2a, 0xb1,
0x72, 0x31, 0x3b, 0x26, 0x67, 0x1b, 0xe9, 0x73, 0xf1, 0xb8, 0x20, 0x59, 0x2c, 0x24, 0xcf, 0xc5,
0x1e, 0x9c, 0x9a, 0x97, 0x02, 0xb2, 0x91, 0x43, 0x0f, 0x68, 0xbd, 0x1e, 0x90, 0x25, 0x05, 0x61,
0x01, 0x95, 0x2b, 0x31, 0x24, 0xb1, 0x81, 0x9d, 0x66, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xc3,
0xc3, 0x86, 0x7b, 0xbf, 0x00, 0x00, 0x00,
......@@ -269,10 +269,12 @@ type LaunchSignature struct {
KeyHash [][]byte `protobuf:"bytes,4,rep,name=keyHash,proto3" json:"keyHash,omitempty"`
// / The signing sequence generated on-the-fly by the platform
Sequence []uint32 `protobuf:"varint,5,rep,name=sequence" json:"sequence,omitempty"`
// / The ttp can be nil if no ttp is available for this signature
Ttp *LaunchSignature_TTP `protobuf:"bytes,6,opt,name=ttp" json:"ttp,omitempty"`
// / The cryptographic object of the signature of this structure (seal and errorCode excepted) by the platform, for data certification.
// / The signature is computed using auth.SignStructure function:
// / PKCS1v15 + SHA512 hash of the string representation of the structure
Seal []byte `protobuf:"bytes,6,opt,name=seal,proto3" json:"seal,omitempty"`
Seal []byte `protobuf:"bytes,10,opt,name=seal,proto3" json:"seal,omitempty"`
func (m *LaunchSignature) Reset() { *m = LaunchSignature{} }
......@@ -287,6 +289,24 @@ func (m *LaunchSignature) GetErrorCode() *ErrorCode {
return nil
func (m *LaunchSignature) GetTtp() *LaunchSignature_TTP {
if m != nil {
return m.Ttp
return nil
// / TTP is the ttp associated to this signature, that should be contacted in case of error
type LaunchSignature_TTP struct {
Addrport string `protobuf:"bytes,1,opt,name=addrport" json:"addrport,omitempty"`
Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"`
func (m *LaunchSignature_TTP) Reset() { *m = LaunchSignature_TTP{} }
func (m *LaunchSignature_TTP) String() string { return proto.CompactTextString(m) }
func (*LaunchSignature_TTP) ProtoMessage() {}
func (*LaunchSignature_TTP) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12, 0} }
func init() {
proto.RegisterType((*RegisterRequest)(nil), "api.RegisterRequest")
proto.RegisterType((*ErrorCode)(nil), "api.ErrorCode")
......@@ -301,6 +321,7 @@ func init() {
proto.RegisterType((*User)(nil), "api.User")
proto.RegisterType((*ReadySignRequest)(nil), "api.ReadySignRequest")
proto.RegisterType((*LaunchSignature)(nil), "api.LaunchSignature")
proto.RegisterType((*LaunchSignature_TTP)(nil), "api.LaunchSignature.TTP")
proto.RegisterEnum("api.ErrorCode_Code", ErrorCode_Code_name, ErrorCode_Code_value)
......@@ -622,47 +643,53 @@ var _Platform_serviceDesc = grpc.ServiceDesc{
var fileDescriptor0 = []byte{
// 666 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x54, 0x5d, 0x6f, 0xda, 0x4a,
0x10, 0xc5, 0xc1, 0xe1, 0x63, 0xc0, 0xc4, 0x5a, 0x88, 0xe2, 0x8b, 0x74, 0xa5, 0xdc, 0xd5, 0x7d,
0x88, 0xee, 0x95, 0xa0, 0xa5, 0x0f, 0x95, 0xda, 0x97, 0x12, 0x4a, 0x93, 0x54, 0x6d, 0x1a, 0x19,
0x48, 0x9f, 0x5d, 0x7b, 0x13, 0xdc, 0x60, 0xaf, 0xbb, 0xbb, 0x7e, 0xc8, 0xbf, 0xa8, 0xfa, 0x1f,
0xfa, 0x37, 0xdb, 0xee, 0xae, 0x3f, 0x0a, 0x94, 0x56, 0xe1, 0x01, 0xb4, 0x3b, 0x33, 0x67, 0xce,
0x9c, 0x3d, 0x03, 0xfc, 0x1d, 0xdc, 0x70, 0x3e, 0x54, 0x5f, 0xc9, 0xd0, 0x4b, 0xc2, 0x61, 0xb2,
0xf2, 0xc4, 0x0d, 0x65, 0xd1, 0x20, 0x61, 0x54, 0x50, 0x54, 0x95, 0x77, 0xf8, 0x31, 0x1c, 0xb8,
0xe4, 0x36, 0xe4, 0x82, 0x30, 0x97, 0x7c, 0x4a, 0x09, 0x17, 0xc8, 0x82, 0x7d, 0x12, 0x79, 0xe1,
0xca, 0x31, 0x8e, 0x8d, 0x93, 0x26, 0x3a, 0x80, 0x3a, 0xcb, 0x22, 0xce, 0x9e, 0xba, 0xc0, 0x5f,
0x0d, 0x68, 0x4e, 0x19, 0xa3, 0x6c, 0x42, 0x03, 0x82, 0xfe, 0x01, 0xd3, 0x97, 0xbf, 0x3a, 0xb9,
0x33, 0xea, 0x0e, 0x24, 0xe8, 0xa0, 0x8c, 0x0e, 0x74, 0x8a, 0x44, 0x88, 0x08, 0xe7, 0xde, 0x2d,
0xc9, 0x11, 0x02, 0x30, 0x75, 0xa0, 0x05, 0xf5, 0xd9, 0x62, 0x32, 0x99, 0xce, 0x66, 0x76, 0x05,
0x01, 0xd4, 0x2e, 0x2e, 0xaf, 0xc7, 0xee, 0x99, 0x6d, 0xa8, 0xc0, 0xe9, 0xf8, 0xe5, 0x78, 0x31,
0x3f, 0xb7, 0xf7, 0xd4, 0xe1, 0xfd, 0xd8, 0xbd, 0xbc, 0xb8, 0x3c, 0xb3, 0xab, 0xa8, 0xab, 0xb2,
0xe6, 0x53, 0xd7, 0xb5, 0xbf, 0x17, 0x1f, 0x03, 0xf5, 0xa0, 0x3e, 0xbf, 0x78, 0x3b, 0x7d, 0xb7,
0x98, 0xdb, 0xdf, 0xca, 0x5b, 0xfc, 0x3f, 0xb4, 0xc6, 0xa9, 0x58, 0xfe, 0x66, 0x2c, 0x79, 0x14,
0xf4, 0x8e, 0xc4, 0x39, 0xa5, 0x7f, 0xa1, 0x53, 0xe8, 0x40, 0x82, 0x05, 0x27, 0x0c, 0x49, 0x42,
0xfe, 0x2a, 0x24, 0xb1, 0x98, 0x10, 0x26, 0xb2, 0x22, 0x5c, 0x87, 0xfd, 0x69, 0x94, 0x88, 0x7b,
0x7c, 0x0d, 0xdd, 0x2b, 0xca, 0xc5, 0x84, 0xc6, 0x82, 0x79, 0xbe, 0x28, 0x7a, 0xb4, 0xc1, 0x5c,
0x7a, 0x7c, 0xa9, 0xb3, 0xdb, 0xc8, 0x86, 0xc6, 0x4d, 0xb8, 0x22, 0xb1, 0x17, 0xe5, 0x83, 0xa3,
0x0e, 0xd4, 0x78, 0x78, 0x1b, 0x13, 0xe6, 0x54, 0x8f, 0xab, 0x99, 0xb6, 0x3e, 0x8d, 0x22, 0xd9,
0xc4, 0x31, 0x75, 0x03, 0x0c, 0xe8, 0x8c, 0xec, 0x82, 0x4d, 0xd3, 0x30, 0xc8, 0x49, 0x3c, 0x87,
0x46, 0x91, 0x20, 0xd5, 0x6f, 0x92, 0x42, 0x6c, 0x1d, 0x6e, 0x8d, 0x3a, 0x9b, 0x4f, 0xa0, 0x8a,
0x3f, 0x72, 0x9a, 0xcd, 0xd9, 0xc6, 0xaf, 0xa0, 0xf7, 0x9a, 0x86, 0xf1, 0x4c, 0xb2, 0xf0, 0x44,
0xca, 0x48, 0xd1, 0xa2, 0x07, 0x6d, 0x3f, 0x07, 0x5d, 0x94, 0xad, 0x54, 0x6d, 0x42, 0x59, 0xf6,
0xf0, 0x96, 0x14, 0x64, 0x2f, 0x4c, 0x32, 0xe6, 0xd8, 0x03, 0x4b, 0xa9, 0x24, 0x89, 0xc4, 0xc4,
0x17, 0x24, 0x78, 0x08, 0x93, 0xed, 0x1e, 0x99, 0x26, 0x47, 0x72, 0x38, 0xae, 0x15, 0x51, 0x35,
0x4d, 0x5d, 0xa3, 0xa0, 0xf1, 0x0b, 0x30, 0xf5, 0x43, 0x48, 0x91, 0xee, 0xc8, 0xfd, 0xf9, 0x4f,
0x5d, 0xcb, 0x97, 0xcc, 0x00, 0xd6, 0x68, 0x95, 0x84, 0x95, 0x9a, 0x16, 0x3e, 0x01, 0xdb, 0x25,
0x5e, 0x70, 0xaf, 0xa6, 0xfd, 0xe3, 0xa0, 0xf8, 0x8b, 0x01, 0x07, 0x6f, 0xbc, 0x34, 0xf6, 0x97,
0xa5, 0x32, 0x0f, 0x99, 0xe8, 0x10, 0x2c, 0x5e, 0xe4, 0xaf, 0x8d, 0x24, 0x7b, 0x04, 0xd4, 0x4f,
0xd5, 0xbb, 0x6a, 0xda, 0x55, 0x4d, 0x7b, 0x6d, 0x0e, 0x53, 0x92, 0xd5, 0xfe, 0xe0, 0x8a, 0x55,
0xec, 0x13, 0x67, 0x5f, 0xde, 0x58, 0x8a, 0x3e, 0x27, 0xde, 0xca, 0xa9, 0xa9, 0x82, 0xd1, 0xe7,
0x2a, 0x34, 0xae, 0xf2, 0x9d, 0x45, 0x23, 0x68, 0x14, 0x06, 0x45, 0x3d, 0x4d, 0x63, 0x6b, 0x6f,
0xfb, 0x5b, 0xe4, 0x70, 0x05, 0x0d, 0xc1, 0x54, 0x1b, 0x80, 0x6c, 0x1d, 0x59, 0x5b, 0x86, 0x7e,
0x77, 0x03, 0x21, 0x73, 0xbc, 0x2c, 0xf8, 0x0f, 0x60, 0x11, 0xb3, 0xa2, 0x0d, 0x64, 0x80, 0xca,
0xf0, 0x3b, 0xc0, 0x9f, 0x41, 0x7b, 0x7d, 0x05, 0x90, 0xa3, 0x33, 0x76, 0x6c, 0xc5, 0x8e, 0xda,
0xa7, 0xd0, 0x5a, 0xb3, 0x39, 0x3a, 0xd2, 0x09, 0xbf, 0x1a, 0xbf, 0x6f, 0xe9, 0x40, 0x71, 0x2b,
0x0b, 0x4f, 0xc1, 0xda, 0xb0, 0x2f, 0xfa, 0x4b, 0x67, 0xec, 0xb2, 0x74, 0x1f, 0x95, 0x56, 0x2a,
0x5d, 0x8a, 0x2b, 0x8f, 0x0c, 0x49, 0xbc, 0x59, 0xba, 0x02, 0x1d, 0xe6, 0x42, 0x6c, 0xba, 0xa4,
0x9f, 0x29, 0xbc, 0xe5, 0x08, 0x5c, 0xf9, 0x50, 0xd3, 0x7f, 0x9d, 0x4f, 0x7e, 0x04, 0x00, 0x00,
0xff, 0xff, 0x9e, 0x01, 0x0b, 0x56, 0x5b, 0x05, 0x00, 0x00,
// 768 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x55, 0xdd, 0x6e, 0xd3, 0x4a,
0x10, 0x4e, 0x62, 0x37, 0x3f, 0x93, 0x26, 0xb5, 0x36, 0x39, 0xe7, 0xf8, 0x44, 0xea, 0x51, 0xb5,
0x3a, 0x12, 0x55, 0x85, 0x92, 0x2a, 0x08, 0x10, 0xdc, 0xa5, 0x21, 0x6a, 0x8b, 0x4a, 0xa9, 0x36,
0x09, 0x48, 0x5c, 0x20, 0x19, 0x7b, 0xdb, 0x98, 0x26, 0xb6, 0xd9, 0xdd, 0x08, 0xf5, 0x8e, 0x47,
0xe0, 0x61, 0x10, 0x8f, 0x07, 0xec, 0xae, 0x7f, 0xe2, 0x84, 0x08, 0xa9, 0xbd, 0x70, 0x77, 0x7e,
0x76, 0xe6, 0x9b, 0x6f, 0x67, 0x26, 0xb0, 0xef, 0x5d, 0x73, 0xde, 0x53, 0x9f, 0xa8, 0xe7, 0x44,
0x7e, 0x2f, 0x9a, 0x3b, 0xe2, 0x3a, 0x64, 0x8b, 0x6e, 0xc4, 0x42, 0x11, 0x22, 0x43, 0xea, 0xf0,
0x00, 0xf6, 0x08, 0xbd, 0xf1, 0xb9, 0xa0, 0x8c, 0xd0, 0x4f, 0x4b, 0xca, 0x05, 0x6a, 0xc3, 0x0e,
0x5d, 0x38, 0xfe, 0xdc, 0x2e, 0x1e, 0x14, 0x0f, 0x6b, 0x24, 0x16, 0x90, 0x0d, 0x15, 0x16, 0x3b,
0xd8, 0x25, 0xad, 0x4f, 0x45, 0xfc, 0xad, 0x08, 0xb5, 0x11, 0x63, 0x21, 0x1b, 0x86, 0x1e, 0x45,
0x0f, 0xc0, 0x74, 0xe5, 0x7f, 0x7d, 0xb9, 0xd9, 0x6f, 0x75, 0x65, 0x92, 0x6e, 0x66, 0xed, 0xaa,
0x0f, 0xd1, 0x0e, 0x2a, 0xe0, 0x82, 0x72, 0xee, 0xdc, 0xd0, 0x34, 0x60, 0x22, 0x62, 0x0f, 0x4c,
0x1d, 0xaa, 0x0e, 0x95, 0xf1, 0x74, 0x38, 0x1c, 0x8d, 0xc7, 0x56, 0x01, 0x01, 0x94, 0xcf, 0x2f,
0xdf, 0x0c, 0xc8, 0xa9, 0x55, 0x54, 0x86, 0x93, 0xc1, 0x8b, 0xc1, 0x74, 0x72, 0x66, 0x95, 0x94,
0xf0, 0x76, 0x40, 0x2e, 0xcf, 0x2f, 0x4f, 0x2d, 0x03, 0xb5, 0x94, 0xd7, 0x64, 0x44, 0x88, 0xf5,
0x33, 0xfd, 0x2b, 0xca, 0x82, 0x2a, 0x93, 0xf3, 0x57, 0xa3, 0xd7, 0xd3, 0x89, 0xf5, 0x23, 0xd3,
0xe2, 0x67, 0x50, 0x1f, 0x2c, 0xc5, 0xec, 0xcf, 0x55, 0x4b, 0xad, 0x08, 0x6f, 0x69, 0x90, 0x40,
0x8c, 0x05, 0x7c, 0x0c, 0xcd, 0x94, 0x34, 0xea, 0x4d, 0x39, 0x65, 0xe8, 0x3f, 0x00, 0x77, 0xee,
0xd3, 0x40, 0x0c, 0x29, 0x13, 0x49, 0x88, 0x9c, 0x06, 0x57, 0x60, 0x67, 0xb4, 0x88, 0xc4, 0x1d,
0xfe, 0x0c, 0xad, 0xab, 0x90, 0x8b, 0x61, 0x18, 0x08, 0xe6, 0xb8, 0x22, 0xcd, 0x8e, 0xc0, 0x9c,
0x39, 0x7c, 0xa6, 0x6f, 0xee, 0x12, 0x7d, 0x46, 0x1d, 0xa8, 0x5e, 0xfb, 0x73, 0x1a, 0x38, 0x8b,
0x94, 0xa1, 0x4c, 0x46, 0x7f, 0x43, 0x99, 0xfb, 0x37, 0x01, 0x65, 0xb6, 0x71, 0x60, 0x48, 0x4b,
0x22, 0x29, 0x52, 0xdd, 0x70, 0xb1, 0x90, 0x69, 0x6d, 0x33, 0x26, 0x35, 0x11, 0xf1, 0x21, 0xa0,
0x53, 0xba, 0x2d, 0xef, 0x72, 0xe9, 0x7b, 0x09, 0x62, 0x7d, 0xc6, 0x17, 0x50, 0x4d, 0xdd, 0xd0,
0x43, 0xa8, 0xd1, 0xf4, 0xf1, 0xb4, 0x53, 0xbd, 0xdf, 0x5c, 0x7f, 0x52, 0xb2, 0x72, 0x50, 0xd1,
0x3e, 0xf2, 0x30, 0x26, 0x4b, 0x56, 0xa1, 0xce, 0xf8, 0x3d, 0xb4, 0x5f, 0x86, 0x7e, 0x30, 0x96,
0xf8, 0x1c, 0xb1, 0x64, 0x34, 0xcd, 0x8c, 0x61, 0xd7, 0x4d, 0xb2, 0x4c, 0x57, 0x08, 0xd6, 0x74,
0x2a, 0x5e, 0x14, 0xb2, 0xb8, 0xe1, 0x1a, 0x44, 0x9f, 0x51, 0x13, 0x4a, 0x7e, 0x94, 0x54, 0x2d,
0x4f, 0xf8, 0x4b, 0x11, 0x1a, 0xea, 0x09, 0x24, 0xe4, 0x80, 0xba, 0x82, 0x7a, 0xf7, 0xc4, 0xbc,
0x89, 0xa3, 0xb4, 0x05, 0xc7, 0xbe, 0x64, 0x89, 0x6b, 0xae, 0x55, 0xb0, 0x9a, 0x0e, 0xa6, 0x72,
0x12, 0xad, 0xc6, 0xef, 0xc0, 0xd4, 0x4d, 0x20, 0xc9, 0xbf, 0xa5, 0x77, 0x67, 0xab, 0x77, 0x4c,
0xc5, 0x55, 0x73, 0x95, 0xf2, 0xcd, 0xb5, 0x51, 0x4a, 0x56, 0xae, 0xb9, 0x2a, 0x17, 0x3f, 0x01,
0x8b, 0x50, 0xc7, 0xbb, 0x53, 0xfc, 0xdd, 0x83, 0x3a, 0xfc, 0xbd, 0x04, 0x7b, 0x17, 0xce, 0x32,
0x70, 0x67, 0x19, 0xf3, 0xf7, 0x24, 0xe6, 0x7f, 0x68, 0xf0, 0xf4, 0x6a, 0x8e, 0x99, 0x75, 0xa5,
0xc2, 0xe2, 0x85, 0xee, 0x52, 0xb5, 0x98, 0x2e, 0xdc, 0xd0, 0x85, 0xaf, 0xe9, 0xf2, 0xbc, 0x98,
0xb2, 0xd8, 0x1c, 0x2f, 0xb2, 0xc5, 0xb9, 0x2a, 0x2a, 0x70, 0xa9, 0xbd, 0x23, 0x4d, 0x0d, 0x92,
0xc9, 0xe8, 0x08, 0x0c, 0x21, 0x22, 0xbb, 0xac, 0x71, 0xda, 0x1a, 0xe7, 0x46, 0x41, 0xdd, 0xc9,
0xe4, 0x8a, 0x28, 0x27, 0xc5, 0x1c, 0xa7, 0xce, 0xdc, 0x86, 0xb8, 0xf1, 0xd4, 0xb9, 0xf3, 0x18,
0x0c, 0x69, 0x57, 0x29, 0x1c, 0xcf, 0x63, 0x9a, 0xd8, 0x98, 0xa8, 0x4c, 0xce, 0xa6, 0xae, 0xb4,
0x9a, 0xba, 0xfe, 0x57, 0x03, 0xaa, 0x57, 0xc9, 0xa2, 0x44, 0x7d, 0xa8, 0xa6, 0x83, 0x8e, 0xda,
0x1a, 0xc2, 0xc6, 0xb2, 0xec, 0x6c, 0x10, 0x88, 0x0b, 0xa8, 0x07, 0xa6, 0xda, 0x2b, 0xc8, 0xd2,
0x96, 0xdc, 0x8a, 0xe9, 0xb4, 0xd6, 0x22, 0xc4, 0x9b, 0x43, 0x5e, 0x38, 0x02, 0x98, 0x06, 0x2c,
0x4d, 0x03, 0x71, 0x40, 0xb5, 0x2c, 0xb6, 0x04, 0x7f, 0x0e, 0xbb, 0xf9, 0xf5, 0x81, 0x62, 0x5e,
0xb6, 0x6c, 0x94, 0x2d, 0x77, 0x9f, 0x42, 0x3d, 0xb7, 0x01, 0xd0, 0x3f, 0xda, 0xe1, 0xf7, 0x9d,
0xd0, 0x69, 0x68, 0x43, 0xaa, 0x95, 0x17, 0x4f, 0xa0, 0xb1, 0x36, 0xc2, 0xe8, 0x5f, 0xed, 0xb1,
0x6d, 0xac, 0x3b, 0x28, 0x1b, 0x8e, 0x6c, 0x20, 0x71, 0xe1, 0xb8, 0x28, 0x81, 0xd7, 0xb2, 0x3e,
0x46, 0x7f, 0x25, 0x44, 0xac, 0xf7, 0x75, 0xa7, 0xbd, 0xed, 0x91, 0x71, 0xe1, 0x43, 0x59, 0xff,
0x5e, 0x3d, 0xfa, 0x15, 0x00, 0x00, 0xff, 0xff, 0x31, 0x18, 0xb6, 0x50, 0xd0, 0x06, 0x00, 0x00,
......@@ -144,8 +144,15 @@ message LaunchSignature {
repeated bytes keyHash = 4;
/// The signing sequence generated on-the-fly by the platform
repeated uint32 sequence = 5;
/// TTP is the ttp associated to this signature, that should be contacted in case of error
message TTP {
string addrport = 1;
bytes hash = 2;
/// The ttp can be nil if no ttp is available for this signature
TTP ttp = 6;
/// The cryptographic object of the signature of this structure (seal and errorCode excepted) by the platform, for data certification.
/// The signature is computed using auth.SignStructure function:
/// PKCS1v15 + SHA512 hash of the string representation of the structure
bytes seal = 6;
bytes seal = 10;
......@@ -22,12 +22,13 @@ type PlatformID struct {
// Initialize creates and saves the platform's private key and root certificate to a PEM format.
// If ca and rKey are not nil, they will be used as the root certificate and root private key instead of creating a ones.
// The files are saved at the specified path by viper.
func Initialize(v *viper.Viper, ca *x509.Certificate, rKey *rsa.PrivateKey) error {
// The returned `hash` is the SHA-512 hash of the generated certificate.
func Initialize(v *viper.Viper, ca *x509.Certificate, rKey *rsa.PrivateKey) (hash []byte, err error) {
// Generate the private key.
key, err := auth.GeneratePrivateKey(v.GetInt("key_size"))
if err != nil {
return err
return nil, err
var cert []byte
......@@ -50,24 +51,25 @@ func Initialize(v *viper.Viper, ca *x509.Certificate, rKey *rsa.PrivateKey) erro
if err != nil {
return err
return nil, err
// Create missing folders, if needed
err = os.MkdirAll(path, os.ModeDir|0700)
if err != nil {
return err
return nil, err
// Convert the private key to a PEM format, and save it.
keyPem := auth.PrivateKeyToPEM(key)
err = ioutil.WriteFile(keyPath, keyPem, 0600)
if err != nil {
return err
return nil, err
// Save the root certificate.
return ioutil.WriteFile(certPath, cert, 0600)
rawCert, _ := auth.PEMToCertificate(cert) // TODO optimize this...
return auth.GetCertificateHash(rawCert), ioutil.WriteFile(certPath, cert, 0600)
// Start fetches the platform's private rsa key and root certificate, and create a PlatformID accordingly.
......@@ -8,10 +8,9 @@ import (
var (
......@@ -36,9 +35,9 @@ func TestInitialize(t *testing.T) {
certPath := filepath.Join(path, RootCAFileName)
v := common.MockViper("key_size", 1024, "validity", 365, "country", "country", "organization", "organization", "unit", "unit", "cn", "cn", "path", path)
err := Initialize(v, nil, nil)
hash, err := Initialize(v, nil, nil)
if err != nil {
if err != nil || hash == nil {
......@@ -60,8 +59,8 @@ func Example() {
// Generate root certificate and key
v := common.MockViper("key_size", 1024, "validity", 365, "country", "UK", "organization", "DFSS", "unit", "unit", "cn", "ROOT", "path", path)
err := Initialize(v, nil, nil)
if err != nil {
hash, err := Initialize(v, nil, nil)
if err != nil || hash == nil {
......@@ -79,7 +78,7 @@ func Example() {
// Generate child certificate and key
childPath := filepath.Join(path, "child")
v = common.MockViper("key_size", 1024, "validity", 10, "country", "FR", "organization", "DFSS", "unit", "unit", "cn", "CHILD", "path", childPath)
err = Initialize(v, pid.RootCA, pid.Pkey)
_, err = Initialize(v, pid.RootCA, pid.Pkey)
if err != nil {
......@@ -107,7 +106,7 @@ func CheckFile(path, name string) {
func TestStart(t *testing.T) {
path, _ := ioutil.TempDir("", "")
v := common.MockViper("key_size", 1024, "validity", 365, "country", "country", "organization", "organization", "unit", "unit", "cn", "cn", "path", path)
_ = Initialize(v, nil, nil)
_, _ = Initialize(v, nil, nil)
pid, err := Start(path)
if err != nil {
...... aabbcc 0123456789
package authority
import (
// TTPHolder stores available TTPs (trusted third parties)
type TTPHolder struct {
ttps []*api.LaunchSignature_TTP
next int
mutex *sync.Mutex
// NewTTPHolder loads available TTPs from the specified file.
// The format of this file should be as-is:
// <addr ttp 1>[:<port ttp 1] <SHA-512 hash of the ttp certificate (hex format)>\n
// ...
// Example: see testdata/ttps.
// If an error occurs during the retrieval of the file, an empty TTPHolder will be provided.
// If the file is corrupted (wrong format), and error will be thrown.
func NewTTPHolder(filename string) (*TTPHolder, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
data = []byte{}
lines := strings.Split(string(data), "\n")
ttps := make([]*api.LaunchSignature_TTP, len(lines)-1) // -1 to ignore last string (empty)
for i := 0; i < len(lines)-1; i++ {
line := lines[i]
words := strings.Split(line, " ")
if len(words) < 2 {
return nil, errors.New("corrupted ttp file: not enough words at line " + strconv.Itoa(i))
hash, err := hex.DecodeString(words[1])
if err != nil {
return nil, errors.New("corrupted ttp file: invalid hash at line " + strconv.Itoa(i))
ttps[i] = &api.LaunchSignature_TTP{
Addrport: words[0],
Hash: hash,
holder := &TTPHolder{
ttps: ttps,
next: 0,
mutex: &sync.Mutex{},
return holder, nil
// Nb returns the number of loaded TTP in this holder.
func (h *TTPHolder) Nb() int {
return len(h.ttps)
// Get returns a TTP from the TTP holder.
// It is thread-safe, and base on a round-robin system.
// If the TTPHolder is empty, returns nil.
func (h *TTPHolder) Get() *api.LaunchSignature_TTP {
if h.Nb() == 0 {
return nil
defer h.mutex.Unlock()
if len(h.ttps) == { = 0
ttp := h.ttps[]
return ttp
// Add adds the provided TTP to the TTP holder.
// It is thread-safe.
func (h *TTPHolder) Add(addrport string, hash []byte) {
defer h.mutex.Unlock()
h.ttps = append(h.ttps, &api.LaunchSignature_TTP{
Addrport: addrport,
Hash: hash,
// Save saves the TTP holder in a file, respecting the same format as presented in the loader.
func (h *TTPHolder) Save(filename string) error {
data := ""
for _, ttp := range h.ttps {
data += ttp.Addrport + " " + hex.EncodeToString(ttp.Hash) + "\n"
return ioutil.WriteFile(filename, []byte(data), 0600)
package authority
import (