@ -20,99 +20,81 @@
package signify
package signify
import (
import (
"bytes"
"crypto/ed25519"
"encoding/base64"
"encoding/base64"
"errors"
"errors"
"fmt"
"fmt"
"io/ioutil"
"io/ioutil"
"os"
"strings"
"strings"
"time"
"time"
"crypto/ed25519"
)
)
var (
var (
errInvalidKeyHeader = errors . New ( "I ncorrect key header" )
errInvalidKeyHeader = errors . New ( "i ncorrect key header" )
errInvalidKeyLength = errors . New ( "invalid, key length != 104" )
errInvalidKeyLength = errors . New ( "invalid, key length != 104" )
)
)
func parsePrivateKey ( key string ) ( ed25519 . PrivateKey , [ ] byte , [ ] byte , error ) {
func parsePrivateKey ( key string ) ( k ed25519 . PrivateKey , header [ ] byte , keyNum [ ] byte , err error ) {
keydata , err := base64 . StdEncoding . DecodeString ( key )
keydata , err := base64 . StdEncoding . DecodeString ( key )
if err != nil {
if err != nil {
return nil , nil , nil , err
return nil , nil , nil , err
}
}
if len ( keydata ) != 104 {
if len ( keydata ) != 104 {
return nil , nil , nil , errInvalidKeyLength
return nil , nil , nil , errInvalidKeyLength
}
}
if string ( keydata [ : 2 ] ) != "Ed" {
if string ( keydata [ : 2 ] ) != "Ed" {
return nil , nil , nil , errInvalidKeyHeader
return nil , nil , nil , errInvalidKeyHeader
}
}
return ed25519 . PrivateKey ( keydata [ 40 : ] ) , keydata [ : 2 ] , keydata [ 32 : 40 ] , nil
return ed25519 . PrivateKey ( keydata [ 40 : ] ) , keydata [ : 2 ] , keydata [ 32 : 40 ] , nil
}
}
func commentHasManyLines ( comment string ) bool {
// SignFile creates a signature of the input file.
firstLFIndex := strings . IndexByte ( comment , 10 )
//
return ( firstLFIndex >= 0 && firstLFIndex < len ( comment ) - 1 )
// This accepts base64 keys in the format created by the 'signify' tool.
}
// The signature is written to the 'output' file.
func SignFile ( input string , output string , key string , untrustedComment string , trustedComment string ) error {
// SignifySignFile creates a signature of the input file.
// Pre-check comments and ensure they're set to something.
func SignifySignFile ( input string , output string , key string , unTrustedComment string , trustedComment string ) error {
if strings . IndexByte ( untrustedComment , '\n' ) >= 0 {
in , err := os . Open ( input )
return errors . New ( "untrusted comment must not contain newline" )
if err != nil {
return err
}
}
defer in . Close ( )
if strings . IndexByte ( trustedComment , '\n' ) >= 0 {
return errors . New ( "trusted comment must not contain newline" )
out , err := os . Create ( output )
}
if err != nil {
if untrustedComment == "" {
return err
untrustedComment = "verify with " + input + ".pub"
}
if trustedComment == "" {
trustedComment = fmt . Sprintf ( "timestamp:%d" , time . Now ( ) . Unix ( ) )
}
}
defer out . Close ( )
skey , header , keyNum , err := parsePrivateKey ( key )
filedata , err := ioutil . ReadFile ( input )
if err != nil {
if err != nil {
return err
return err
}
}
skey , header , keyNum , err := parsePrivateKey ( key )
filedata , err := ioutil . ReadAll ( in )
if err != nil {
if err != nil {
return err
return err
}
}
// Create the main data signature.
rawSig := ed25519 . Sign ( skey , filedata )
rawSig := ed25519 . Sign ( skey , filedata )
var dataSig [ ] byte
var sigdata [ ] byte
dataSig = append ( dataSig , header ... )
sigdata = append ( sigdata , header ... )
dataSig = append ( dataSig , keyNum ... )
sigdata = append ( sigdata , keyNum ... )
dataSig = append ( dataSig , rawSig ... )
sigdata = append ( sigdata , rawSig ... )
// Create the comment signature.
// Check that the trusted comment fits in one line
var commentSigInput [ ] byte
if commentHasManyLines ( unTrustedComment ) {
commentSigInput = append ( commentSigInput , rawSig ... )
return errors . New ( "untrusted comment must fit on a single line" )
commentSigInput = append ( commentSigInput , [ ] byte ( trustedComment ) ... )
}
commentSig := ed25519 . Sign ( skey , commentSigInput )
if unTrustedComment == "" {
// Create the output file.
unTrustedComment = "verify with " + input + ".pub"
var out = new ( bytes . Buffer )
}
fmt . Fprintln ( out , "untrusted comment:" , untrustedComment )
out . WriteString ( fmt . Sprintf ( "untrusted comment: %s\n%s\n" , unTrustedComment , base64 . StdEncoding . EncodeToString ( sigdata ) ) )
fmt . Fprintln ( out , base64 . StdEncoding . EncodeToString ( dataSig ) )
fmt . Fprintln ( out , "trusted comment:" , trustedComment )
// Add the trusted comment if unavailable
fmt . Fprintln ( out , base64 . StdEncoding . EncodeToString ( commentSig ) )
if trustedComment == "" {
return ioutil . WriteFile ( output , out . Bytes ( ) , 0644 )
trustedComment = fmt . Sprintf ( "timestamp:%d" , time . Now ( ) . Unix ( ) )
}
// Check that the trusted comment fits in one line
if commentHasManyLines ( trustedComment ) {
return errors . New ( "trusted comment must fit on a single line" )
}
var sigAndComment [ ] byte
sigAndComment = append ( sigAndComment , rawSig ... )
sigAndComment = append ( sigAndComment , [ ] byte ( trustedComment ) ... )
out . WriteString ( fmt . Sprintf ( "trusted comment: %s\n%s\n" , trustedComment , base64 . StdEncoding . EncodeToString ( ed25519 . Sign ( skey , sigAndComment ) ) ) )
return nil
}
}