Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
mpcs
dfss
Commits
5d2ebd9f
Commit
5d2ebd9f
authored
May 25, 2016
by
Axel
Browse files
[t][c] Add recover procedure
parent
3ee03370
Pipeline
#2308
passed with stage
Changes
16
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
dfssc/cmd/recover.go
0 → 100644
View file @
5d2ebd9f
package
cmd
import
(
"fmt"
"os"
"dfss/dfssc/sign"
"github.com/spf13/cobra"
)
var
recoverCmd
=
&
cobra
.
Command
{
Use
:
"recover <f>"
,
Short
:
"try to recover signed contract from recover data file f"
,
Run
:
recover
,
}
func
recover
(
cmd
*
cobra
.
Command
,
args
[]
string
)
{
if
len
(
args
)
!=
1
{
_
=
cmd
.
Usage
()
return
}
var
passphrase
string
_
=
readPassword
(
&
passphrase
,
false
)
filename
:=
args
[
0
]
err
:=
sign
.
Recover
(
filename
,
passphrase
)
if
err
!=
nil
{
fmt
.
Fprintln
(
os
.
Stderr
,
err
)
os
.
Exit
(
1
)
return
}
fmt
.
Println
(
"Successfully recovered signed contract."
)
fmt
.
Println
(
"Check .proof file."
)
}
dfssc/cmd/root.go
View file @
5d2ebd9f
...
...
@@ -58,6 +58,5 @@ func init() {
_
=
viper
.
BindPFlag
(
"timeout"
,
RootCmd
.
PersistentFlags
()
.
Lookup
(
"timeout"
))
// Bind subcommands to root
RootCmd
.
AddCommand
(
dfss
.
VersionCmd
,
registerCmd
,
authCmd
,
newCmd
,
showCmd
,
fetchCmd
,
importCmd
,
exportCmd
,
signCmd
,
unregisterCmd
)
RootCmd
.
AddCommand
(
dfss
.
VersionCmd
,
registerCmd
,
authCmd
,
newCmd
,
showCmd
,
fetchCmd
,
importCmd
,
exportCmd
,
signCmd
,
unregisterCmd
,
recoverCmd
)
}
dfssc/cmd/sign.go
View file @
5d2ebd9f
...
...
@@ -74,6 +74,13 @@ var signCmd = &cobra.Command{
// TODO Warning, integration tests are checking Stdout
fmt
.
Println
(
"Everybody is ready, starting the signature"
,
signatureUUID
)
// Persisting the signatureUUID and ttp addrport in case of a crash, to be able to recover
filename
,
err
=
manager
.
PersistRecoverDataToFile
()
if
err
!=
nil
{
fmt
.
Fprintln
(
os
.
Stderr
,
err
)
os
.
Exit
(
1
)
}
// Signature
manager
.
OnProgressUpdate
=
signProgressFn
err
=
manager
.
Sign
()
...
...
@@ -83,6 +90,13 @@ var signCmd = &cobra.Command{
}
fmt
.
Println
(
"Signature complete! See .proof file for evidences."
)
// deleting the recover data file
err
=
os
.
Remove
(
filename
)
if
err
!=
nil
{
fmt
.
Fprintln
(
os
.
Stderr
,
err
)
os
.
Exit
(
1
)
}
},
}
...
...
dfssc/common/file.go
View file @
5d2ebd9f
...
...
@@ -9,7 +9,6 @@ import (
// UnmarshalDFSSFile decodes a json-encoded DFSS file
func
UnmarshalDFSSFile
(
data
[]
byte
)
(
*
contract
.
JSON
,
error
)
{
c
:=
&
contract
.
JSON
{}
err
:=
json
.
Unmarshal
(
data
,
c
)
if
err
!=
nil
{
...
...
@@ -22,3 +21,25 @@ func UnmarshalDFSSFile(data []byte) (*contract.JSON, error) {
return
c
,
nil
}
// RecoverDataJSON : contains all the necessary information to try and recover a previously signed contract from the ttp
type
RecoverDataJSON
struct
{
SignatureUUID
string
TTPAddrport
string
TTPHash
[]
byte
}
// UnmarshalRecoverDataFile decodes a json-encoded Recover dara file
func
UnmarshalRecoverDataFile
(
data
[]
byte
)
(
*
RecoverDataJSON
,
error
)
{
r
:=
&
RecoverDataJSON
{}
err
:=
json
.
Unmarshal
(
data
,
r
)
if
err
!=
nil
{
return
nil
,
err
}
if
r
.
SignatureUUID
==
""
||
r
.
TTPAddrport
==
""
||
r
.
TTPHash
==
nil
{
return
nil
,
errors
.
New
(
"Invalid recover data file"
)
}
return
r
,
nil
}
dfssc/common/file_test.go
View file @
5d2ebd9f
package
common
import
(
"crypto/sha512"
"encoding/json"
"io/ioutil"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/mgo.v2/bson"
)
func
TestUnmarshalDFSSFile
(
t
*
testing
.
T
)
{
...
...
@@ -16,3 +19,28 @@ func TestUnmarshalDFSSFile(t *testing.T) {
assert
.
Equal
(
t
,
"filename.pdf"
,
c
.
File
.
Name
)
// a tiny test, full unmarshal is tested in dfss/dfssp/contract package
}
func
TestUnmarshalRecoverDataFile
(
t
*
testing
.
T
)
{
bsonUUID
:=
bson
.
NewObjectId
()
uuid
:=
bsonUUID
.
Hex
()
ttpAddrport
:=
"127.0.0.1:0000"
h
:=
sha512
.
Sum512
([]
byte
{
7
})
ttpHash
:=
h
[
:
]
recData
:=
RecoverDataJSON
{
SignatureUUID
:
uuid
,
TTPAddrport
:
ttpAddrport
,
TTPHash
:
ttpHash
,
}
file
,
err
:=
json
.
MarshalIndent
(
recData
,
""
,
" "
)
assert
.
Nil
(
t
,
err
)
unmarshal
,
err
:=
UnmarshalRecoverDataFile
(
file
)
assert
.
Nil
(
t
,
err
)
assert
.
Equal
(
t
,
uuid
,
unmarshal
.
SignatureUUID
)
assert
.
Equal
(
t
,
ttpAddrport
,
unmarshal
.
TTPAddrport
)
assert
.
Equal
(
t
,
ttpHash
,
unmarshal
.
TTPHash
)
}
dfssc/sign/persist.go
View file @
5d2ebd9f
...
...
@@ -6,6 +6,7 @@ import (
"io/ioutil"
cAPI
"dfss/dfssc/api"
"dfss/dfssc/common"
"dfss/dfssp/contract"
)
...
...
@@ -45,5 +46,34 @@ func (m *SignatureManager) PersistSignaturesToFile() error {
return
err
}
return
ioutil
.
WriteFile
(
m
.
mail
+
"-"
+
m
.
contract
.
UUID
+
".proof"
,
proof
,
0600
)
return
ioutil
.
WriteFile
(
m
.
mail
+
"-"
+
m
.
uuid
+
".proof"
,
proof
,
0600
)
}
// PersistRecoverDataToFile : save recover informations to disk.
// returns the file name and an error if any occured
func
(
m
*
SignatureManager
)
PersistRecoverDataToFile
()
(
string
,
error
)
{
// Check content, don't write an empty file
if
len
(
m
.
uuid
)
==
0
||
len
(
m
.
ttpData
.
Addrport
)
==
0
||
len
(
m
.
ttpData
.
Hash
)
==
0
{
return
""
,
fmt
.
Errorf
(
"Invalid recover data. Cannot persist file."
)
}
// Fill JSON struct
recData
:=
common
.
RecoverDataJSON
{
SignatureUUID
:
m
.
uuid
,
TTPAddrport
:
m
.
ttpData
.
Addrport
,
TTPHash
:
m
.
ttpData
.
Hash
,
}
file
,
err
:=
json
.
MarshalIndent
(
recData
,
""
,
" "
)
if
err
!=
nil
{
return
""
,
err
}
filename
:=
m
.
mail
+
"-"
+
m
.
uuid
+
".run"
err
=
ioutil
.
WriteFile
(
filename
,
file
,
0600
)
if
err
!=
nil
{
return
""
,
err
}
return
filename
,
nil
}
dfssc/sign/protocol.go
View file @
5d2ebd9f
...
...
@@ -261,7 +261,7 @@ func (m *SignatureManager) resolve() error {
return
nil
}
dAPI
.
DLog
(
"contacted TTP, received signed contract"
)
return
ioutil
.
WriteFile
(
m
.
mail
+
"-"
+
m
.
contract
.
UUID
+
".proof"
,
response
.
Contract
,
0600
)
return
ioutil
.
WriteFile
(
m
.
mail
+
"-"
+
m
.
uuid
+
".proof"
,
response
.
Contract
,
0600
)
}
// checkPromise : verifies that the promise is valid wrt the expected promises.
...
...
dfssc/sign/recover.go
0 → 100644
View file @
5d2ebd9f
package
sign
import
(
"errors"
"io/ioutil"
"time"
"dfss/dfssc/common"
"dfss/dfssc/security"
tAPI
"dfss/dfsst/api"
"dfss/net"
"golang.org/x/net/context"
)
// Recover : performs a recover attempt using the provided recover file, and the user's passphrase to use secured connection
func
Recover
(
filename
,
passphrase
string
)
error
{
json
,
err
:=
readRecoveryFile
(
filename
)
if
err
!=
nil
{
return
err
}
auth
:=
security
.
NewAuthContainer
(
passphrase
)
_
,
_
,
_
,
err
=
auth
.
LoadFiles
()
if
err
!=
nil
{
return
err
}
ttp
,
err
:=
connectToTTP
(
json
,
auth
)
if
err
!=
nil
{
return
err
}
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
10
*
time
.
Minute
)
defer
cancel
()
response
,
err
:=
ttp
.
Recover
(
ctx
,
&
tAPI
.
RecoverRequest
{
SignatureUUID
:
json
.
SignatureUUID
})
if
err
!=
nil
{
return
err
}
return
treatTTPResponse
(
response
,
auth
,
json
.
SignatureUUID
)
}
// readRecoveryFile : reads the recovery file from disk
func
readRecoveryFile
(
filename
string
)
(
*
common
.
RecoverDataJSON
,
error
)
{
data
,
err
:=
ioutil
.
ReadFile
(
filename
)
if
err
!=
nil
{
return
nil
,
err
}
json
,
err
:=
common
.
UnmarshalRecoverDataFile
(
data
)
if
err
!=
nil
{
return
nil
,
err
}
return
json
,
nil
}
// connectToTTP : connects to the ttp, using the provided recover data and authentication information
func
connectToTTP
(
json
*
common
.
RecoverDataJSON
,
auth
*
security
.
AuthContainer
)
(
tAPI
.
TTPClient
,
error
)
{
// TODO check that the connection spots missing TTP and returns an error quickly enough
conn
,
err
:=
net
.
Connect
(
json
.
TTPAddrport
,
auth
.
Cert
,
auth
.
Key
,
auth
.
CA
,
json
.
TTPHash
)
if
err
!=
nil
{
return
nil
,
err
}
return
tAPI
.
NewTTPClient
(
conn
),
nil
}
// treatTTPResponse : handles the writing of the recoverd contract on the disk if it was recovered.
func
treatTTPResponse
(
response
*
tAPI
.
TTPResponse
,
auth
*
security
.
AuthContainer
,
signatureUUID
string
)
error
{
if
len
(
response
.
Contract
)
==
0
{
return
errors
.
New
(
"Contract was not successfully signed. Impossible to recover."
)
}
return
ioutil
.
WriteFile
(
auth
.
Cert
.
Subject
.
CommonName
+
"-"
+
signatureUUID
+
".proof"
,
response
.
Contract
,
0600
)
}
dfsst/entities/check_request.go
View file @
5d2ebd9f
...
...
@@ -29,13 +29,13 @@ func IsRequestValid(ctx context.Context, promises []*cAPI.Promise) (valid bool,
return
}
sender
,
err
:=
GetSenderHashFromContext
(
ctx
)
if
err
!
=
nil
{
sender
:=
net
.
GetClientHash
(
&
ctx
)
if
sender
=
=
nil
{
valid
=
false
return
}
senderIndex
,
err
=
GetIndexOfSigner
(
promises
[
0
],
sender
)
senderIndex
,
err
:
=
GetIndexOfSigner
(
promises
[
0
],
sender
)
if
err
!=
nil
{
valid
=
false
return
...
...
@@ -76,19 +76,6 @@ func IsPromiseSignedByPlatform(promise *cAPI.Promise) (bool, bson.ObjectId, []Si
return
true
,
signatureUUID
,
signers
}
// GetSenderHashFromContext : creates the sender's certificate hash from the specified context.
func
GetSenderHashFromContext
(
ctx
context
.
Context
)
([]
byte
,
error
)
{
state
,
_
,
ok
:=
net
.
GetTLSState
(
&
ctx
)
if
!
ok
||
len
(
state
.
VerifiedChains
)
==
0
{
return
nil
,
errors
.
New
(
"Empty verified sender certificate"
)
}
cert
:=
state
.
VerifiedChains
[
0
][
0
]
hash
:=
auth
.
GetCertificateHash
(
cert
)
return
hash
,
nil
}
// GetIndexOfSigner : determines the index of the specified signer's hash in the array of signers' hashes.
func
GetIndexOfSigner
(
promise
*
cAPI
.
Promise
,
hash
[]
byte
)
(
uint32
,
error
)
{
for
i
,
h
:=
range
promise
.
Context
.
Signers
{
...
...
dfsst/entities/signatureArchives.go
View file @
5d2ebd9f
package
entities
import
(
"bytes"
"gopkg.in/mgo.v2/bson"
)
...
...
@@ -105,3 +107,15 @@ func NewAbortedSigner(signerIndex, abortIndex uint32) *AbortedSigner {
AbortIndex
:
abortIndex
,
}
}
// ContainsSigner : determines whether or not the specified signer is one of the signers,
// and also returns the sequence id of said signer.
func
(
archives
*
SignatureArchives
)
ContainsSigner
(
hash
[]
byte
)
(
bool
,
uint32
)
{
for
i
,
s
:=
range
archives
.
Signers
{
if
bytes
.
Equal
(
hash
,
s
.
Hash
)
{
return
true
,
uint32
(
i
)
}
}
return
false
,
uint32
(
0
)
}
dfsst/entities/signatureArchives_test.go
View file @
5d2ebd9f
...
...
@@ -25,3 +25,26 @@ func TestArePromisesEqual(t *testing.T) {
equal
=
promise0
.
Equal
(
promise1
)
assert
.
Equal
(
t
,
equal
,
true
)
}
func
TestContainsSigner
(
t
*
testing
.
T
)
{
archives
:=
NewSignatureArchives
(
signatureUUIDBson
,
sequence
,
signersEntities
,
contractDocumentHash
,
seal
)
badHash
:=
[]
byte
{}
present
,
index
:=
archives
.
ContainsSigner
(
badHash
)
assert
.
False
(
t
,
present
)
assert
.
Equal
(
t
,
uint32
(
0
),
index
)
badHash
=
[]
byte
{
0
,
0
,
7
}
present
,
index
=
archives
.
ContainsSigner
(
badHash
)
assert
.
False
(
t
,
present
)
assert
.
Equal
(
t
,
uint32
(
0
),
index
)
goodHash
:=
signersEntities
[
0
]
.
Hash
present
,
index
=
archives
.
ContainsSigner
(
goodHash
)
assert
.
True
(
t
,
present
)
assert
.
Equal
(
t
,
uint32
(
0
),
index
)
goodHash
=
signersEntities
[
1
]
.
Hash
present
,
index
=
archives
.
ContainsSigner
(
goodHash
)
assert
.
True
(
t
,
present
)
assert
.
Equal
(
t
,
uint32
(
1
),
index
)
}
dfsst/resolve/resolve.go
View file @
5d2ebd9f
...
...
@@ -182,8 +182,8 @@ func Solve(manager *entities.ArchivesManager) (bool, []byte) {
func
GenerateSignedContract
(
archives
*
entities
.
SignatureArchives
)
[]
byte
{
var
pseudoContract
string
for
_
,
p
:=
range
archives
.
ReceivedPromises
{
signature
:=
"SIGNATURE FROM SIGNER "
+
string
(
archives
.
Signers
[
p
.
SenderKeyIndex
]
.
Hash
)
signature
+=
" ON SIGNATURE n° "
+
st
rin
g
(
archives
.
ID
)
+
"
\n
"
signature
:=
"SIGNATURE FROM SIGNER "
+
fmt
.
Sprintf
(
"%x"
,
archives
.
Signers
[
p
.
SenderKeyIndex
]
.
Hash
)
signature
+=
" ON SIGNATURE n° "
+
fmt
.
Sp
rin
t
(
archives
.
ID
)
+
"
\n
"
pseudoContract
+=
signature
}
...
...
dfsst/resolve/resolve_test.go
View file @
5d2ebd9f
...
...
@@ -446,8 +446,8 @@ func TestGenerateSignedContract(t *testing.T) {
var
pseudoContract
string
for
_
,
p
:=
range
promises
{
signature
:=
"SIGNATURE FROM SIGNER "
+
string
(
signersEntities
[
p
.
SenderKeyIndex
]
.
Hash
)
signature
+=
" ON SIGNATURE n° "
+
st
rin
g
(
id
)
+
"
\n
"
signature
:=
"SIGNATURE FROM SIGNER "
+
fmt
.
Sprintf
(
"%x"
,
signersEntities
[
p
.
SenderKeyIndex
]
.
Hash
)
signature
+=
" ON SIGNATURE n° "
+
fmt
.
Sp
rin
t
(
id
)
+
"
\n
"
pseudoContract
+=
signature
}
...
...
dfsst/server/server.go
View file @
5d2ebd9f
...
...
@@ -226,8 +226,44 @@ func (server *ttpServer) handleContractGenerationTry(manager *entities.ArchivesM
// Recover route for the TTP.
func
(
server
*
ttpServer
)
Recover
(
ctx
context
.
Context
,
in
*
tAPI
.
RecoverRequest
)
(
*
tAPI
.
TTPResponse
,
error
)
{
// TODO
return
nil
,
nil
if
!
bson
.
IsObjectIdHex
(
in
.
SignatureUUID
)
{
return
nil
,
errors
.
New
(
"Invalid signature uuid."
)
}
bsonUUID
:=
bson
.
ObjectIdHex
(
in
.
SignatureUUID
)
manager
:=
entities
.
NewArchivesManager
(
server
.
DB
)
present
,
archives
:=
manager
.
ContainsSignature
(
bsonUUID
)
if
!
present
{
return
nil
,
errors
.
New
(
"Unknown signature uuid."
)
}
manager
.
Archives
=
archives
contract
,
err
:=
handleRecover
(
ctx
,
manager
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
tAPI
.
TTPResponse
{
Contract
:
contract
},
nil
}
func
handleRecover
(
ctx
context
.
Context
,
manager
*
entities
.
ArchivesManager
)
([]
byte
,
error
)
{
senderHash
:=
net
.
GetClientHash
(
&
ctx
)
if
senderHash
==
nil
{
return
[]
byte
{},
errors
.
New
(
"Bad authentication."
)
}
present
,
senderID
:=
manager
.
Archives
.
ContainsSigner
(
senderHash
)
if
!
present
{
return
[]
byte
{},
errors
.
New
(
"Signer was not part of the signature."
)
}
aborted
:=
manager
.
HasReceivedAbortToken
(
senderID
)
if
aborted
{
return
[]
byte
{},
errors
.
New
(
"Signer was aborted."
)
}
_
,
contract
:=
manager
.
WasContractSigned
()
return
contract
,
nil
}
// GetServer returns the gRPC server.
...
...
dfsst/server/server_test.go
View file @
5d2ebd9f
...
...
@@ -104,8 +104,3 @@ func TestMain(m *testing.M) {
os
.
Exit
(
code
)
}
func
TestAlert
(
t
*
testing
.
T
)
{
// TODO
// This requires the user of a real Alert message
}
tests/sign_test.go
View file @
5d2ebd9f
...
...
@@ -144,25 +144,30 @@ func checkProofFile(t *testing.T, nb int) {
// TestSignContractFailure tests the signature with a faulty client, when contract can't be generated.
// In this test, everything should not work fine, because client3 shutdowns way too early.
func
TestSignContractFailure
(
t
*
testing
.
T
)
{
signatureHelper
(
t
,
"1"
,
0
)
signatureHelper
(
t
,
true
)
}
// TestSignContractSuccess tests the signature with a faulty client, when contract can be generated.
// In this test, everything should not work fine, because client3 shutdowns way too early.
func
TestSignContractSuccess
(
t
*
testing
.
T
)
{
signatureHelper
(
t
,
"2"
,
2
)
signatureHelper
(
t
,
false
)
}
// signatureHelper : launches a parametrized signature, with the number of rounds a client will accomplish before shutting down,
// and the number of proof files expected to be generated.
func
signatureHelper
(
t
*
testing
.
T
,
round
string
,
nbFiles
int
)
{
func
signatureHelper
(
t
*
testing
.
T
,
failure
bool
)
{
// Setup
stop
,
clients
,
contractPath
,
contractFilePath
:=
setupSignature
(
t
)
defer
stop
()
stopBefore
,
expectedProofFile1
,
expectedProofFile2
:=
"1"
,
0
,
0
if
!
failure
{
stopBefore
,
expectedProofFile1
,
expectedProofFile2
=
"2"
,
2
,
1
}
// Configure client3 to be faulty
setLastArg
(
clients
[
2
],
"--stopbefore"
,
true
)
setLastArg
(
clients
[
2
],
round
,
false
)
setLastArg
(
clients
[
2
],
stopBefore
,
false
)
setLastArg
(
clients
[
2
],
"sign"
,
false
)
// Sign!
...
...
@@ -183,6 +188,40 @@ func signatureHelper(t *testing.T, round string, nbFiles int) {
<-
closeChannel
}
checkProofFile
(
t
,
nbFiles
)
checkProofFile
(
t
,
expectedProofFile1
)
filename
:=
checkRecoverFile
(
t
,
"client3@example.com"
)
callRecover
(
newClient
(
clients
[
2
]),
filename
)
_
=
os
.
Remove
(
filename
)
checkProofFile
(
t
,
expectedProofFile2
)
time
.
Sleep
(
time
.
Second
)
return
}
// checkProofFile counts the number of proof file contained in the current directory, and compares it to the nb parameter.
func
checkRecoverFile
(
t
*
testing
.
T
,
mail
string
)
string
{
// Ensure that all the files are present
recoverFile
:=
regexp
.
MustCompile
(
mail
+
`.*\.run`
)
files
,
_
:=
ioutil
.
ReadDir
(
"./"
)
var
filename
string
matches
:=
0
for
_
,
file
:=
range
files
{
if
recoverFile
.
Match
([]
byte
(
file
.
Name
()))
{
matches
++
filename
=
file
.
Name
()
}
}
assert
.
Equal
(
t
,
1
,
matches
,
"Invalid number of recover file(s)"
)
return
filename
}
func
callRecover
(
c
*
exec
.
Cmd
,
filename
string
)
{
// We cannot use "setLastArg" because we need to update arguments
c
.
Args
=
c
.
Args
[
:
(
len
(
c
.
Args
)
-
4
)]
c
.
Args
=
append
(
c
.
Args
,
"recover"
,
filename
)
c
.
Stdin
=
strings
.
NewReader
(
"password
\n
"
)
c
.
Stdout
=
os
.
Stdout
c
.
Stderr
=
os
.
Stderr
_
=
c
.
Run
()
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment