README

go-atum

Go client (and CLI tool) to the Atum trusted timestamping server. So, why use Atum instead of RFC 3161 or ANSI ASC X9.95?

  1. Atum timestamps use XMSSMT hash-based signatures by default, which are safe, even against an adversary with a large quantum computer.
  2. Atum has a simple REST/json based API. See below.

Go example

To create a timestamp on some nonce, run

tsBytes, err := atum.JsonStamp("https://some.atum/server", someNonce)

This returns a Json encoded version of the timestamp. To check whether this timestamp is valid, run

valid, time, tsServer, _ := atum.Verify(tsBytes, nonce)

As anyone can run their own Atum server, you should check whether you should trust the Atum server that signed the timestamp (tsServer in the example above).

By default, the Atum server issues XMSSMT signatures, which are somewhat large in size. To request an Ed25519 signature, which is smaller, but not safe against an attacker with a quantum computer, use

alg := atum.Ed25519
ts, err := atum.SendRequest("https://some.atum/server",
                    atum.Request{
                        Nonce: someNonce,
                        PreferredSigAlg: &alg
                    })

The ts is an *atum.Timestamp, which can be serialized using ts.MarshalText() or simply json.Marshal(ts).

For further documentation, see godoc.

Commandline tool

To create a timestamp on a file some-document (with a default Atum server), run:

atum stamp -f some-document

This will create an some-document.atum-timestamp file.

To check the timestamp, run

atum verify -f some-document

This will tell whether the timestamp is valid and by which server it was set. Like before, anyone can set up an Atum server, so you should check whether you trust the Atum server which set the timestamp.

To check for a specific server, run

atum verify -f some-document -S https://some.atum/server

This will fail if the document is not signed by that specific Atum server.

See atum -h for more options.

Server

Want to run your own Atum server? Check out atumd.

Protocol

An Atum server is a webservice and is identified by the url it runs at. An example of an Atum server is

https://keyshare.privacybydesign.foundation/atumd
Request a timestamp

To request a timestamp for the nonce example nonce, simply POST

{"Nonce": "ZXhhbXBsZSB0b2tlbg=="}

to the Atum server url. If everything is fine, the server will respond with

{
 "Error": null,
 "Stamp": (the json encoded Atum timestamp),
 "Info": null
}

If there is a problem, the Error field will be one missing nonce, nonce is too long, proof of work is missing, proof of work is invalid or too much lag. Also, if helpful, the Info field will include the server information, see below.

Atum timestamp for a nonce

An example of a (json encoded) Atum timestamp (for the nonce example nonce) is

{
 "Time": 1520078016,
 "ServerUrl": "https://metrics.privacybydesign.foundation/atum",
 "Sig": {
  "Alg":"ed25519",
  "Data":"L6Pig67OOXI01YuY8798o3F8RA6ehVI2UFc+wa2X4uOu9f6SrfkATb6ZexUKj8HfrHYTn2fK9Xna9rGAFYyWDg==",
  "PublicKey":"e/nMAJF7nwrvNZRpuJljNpRx+CsT7caaXyn9OX683R8="
}
  • Time contains the unix time when the stamp was set. In this case march 3rd, 2018 at 11:53:36 UTC.
  • ServerUrl contains the url of the server which set the timestamp.
  • Alg is the signature algorithm used. Either ed25519 or xmssmt.
  • PublicKey contains the base64 encoded public key of the private key which was used to create the signature.
  • Data contains a base64 encoded Ed25519 or XMSSMT signature of the unix time (uint64, encoded big endian) concatenated with the nonce.

Note that the timestamp does not include the nonce itself. To check a timestamp, one verifies the signature, but also should verify that the public key belongs to the Atum server. More on this later.

Server information

A GET request to the url of the Atum server, will return a Json object like

{
 "MaxNonceSize": 128,
 "AcceptableLag": 60,
 "DefaultSigAlg": "xmssmt",
 "RequiredProofOfWork": {
   "xmssmt": "sha2bday-16-T3oAQ2oV2VIdO5LqOLyrCsOEOr+86AhOyRnR37Vja8I"
 }
}
  • MaxNonceSize is the maximum size of a nonce in bytes which the Atum server will sign.
  • AcceptableLag is the largest difference in seconds the Atum server will accept between the requested time for a timestamp and the actual time.
  • DefaultSigAlg is the default signature algorithm used. See below.
  • RequiredProofOfWork is a map that lists for which signature algorithms what go-pow proof of work the server requires (if any).

This is the same Json object that might appear in Info field in the response to a timestamp POST request.

Optional request fields

A timestamp request (which is POSTed to the server url) may contain the following optional fields.

  • PreferredSigAlg to specify which kind of signature is preferred.
  • Time to request the time on the timestamp. This can't differ too much from the actual local time (as dictated by AcceptableLag).

For instance, this requests an ed25519 signature for the UNIX time 1520078016.

{
 "Nonce": "ZXhhbXBsZSB0b2tlbg==",
 "Time": 1520078016,
 "PreferredSigAlg": "ed25519"
}
Proof of work

The server can be configured to require a proof of work before it will create a timestamp with a certain signature scheme. By default, ed25519 does not require a proof of work, but xmssmt does.

The go-pow proof of work request is contained in the server information and changes, by default, daily. To fulfil the proof of work, the message that is to be signed for the timestamp (uint64 unix time concatenated with the nonce) must be used as bound data. This also means that Time should be specified in the request. The resulting proof is put in the ProofOfWork field of the POSTed request.

An example for the PoW-request sha2bday-16-T3oAQ2oV2VIdO5LqOLyrCsOEOr+86AhOyRnR37Vja8I is

{
 "Nonce":"4ihcK00BmuSQloyRH1kTrJ1/dfmSN5VkNSlDwEl+S+Lyxa2sfzg+t3v7pW6XhYZ8OBsblWgR+byLujVlnVNhpA==",
 "ProofOfWork":"AAAAAAAAAWIAAAAAAAACmwAAAAAAAAZh",
 "Time":1520081097
}
Atum timestamp of a file (or longer message)

To timestamp a file (which is too long to be a nonce), a hash is used. An example of an Atum timestamp on an old versionof this README is

{
 "Time": 1520081260,
 "ServerUrl": "https://metrics.privacybydesign.foundation/atum",
 "Sig": {
   "Alg": "ed25519",
   "Data": "G/HCNLL/ZGkonGcDX4eIMysPw5Pw49vCsQ3wuFbo4dBd81HG8EGfwBsYBfPFCwyudrOW0jTxbNhcGvQG52VHDA==",
   "PublicKey":"e/nMAJF7nwrvNZRpuJljNpRx+CsT7caaXyn9OX683R8="
 },
 "Hashing": {
  "Hash": "shake256",
  "Prefix": "3hGOzeS3h/Wm9FKa8RbXvrdHNqk/N1ZzFKDdnSpdoqg="
 }
}

The nonce is computed by hashing the base64-decoded Prefix and then the file. Currently only shake256 is supported, which is SHA3's SHAKE-256 where a 64-byte nonce is extracted.

Lookup a public key

To verify an Atum timestamp, a client must check whether the public key is valid for the given Atum server. To do this, she sends a GET request with query parameters alg for the algorithm and pk for the hex encoded public key to <server url>/checkPublicKey. For example, a GET to

https://metrics.privacybydesign.foundation/atum/checkPublicKey?alg=ed25519&pk=7bf9cc00917b9f0aef359469b89963369471f82b13edc69a5f29fd397ebcdd1f

returned

{
 "Trusted": true,
 "Expires": "2018-04-02T14:59:06.164300986+02:00"
}

but

https://metrics.privacybydesign.foundation/atum/checkPublicKey?alg=ed25519&pk=7b

would return

{
 "Trusted": false,
 "Expires": "2018-04-02T14:59:06.164300986+02:00"
}

The Expires field contains the time after which the client should check back with the server whether the public key is still trusted.

Other remarks

  1. Trusted server. Anyone can run an Atum server, which might or might not be honest. It is not sufficient to check that an Atum timestamp is valid: you should ensture that you trust the Atum server by which it was set.
  2. xmssmt or ed25519 The ed25519 signatures are significantly smaller (0.4kB versus 2kB for XMSSMT-SHA2_40/2_512) and faster to create and verify. xmssmt is still very fast, it takes approximately 5ms to create or verify a signature for XMSSMT-SHA2_40/2_512. The big difference is that ed25519 is easily broken by someone in possesion of a moderately sized quantum computer, which we are likely to see within the next 50 years. On the other hand, it seems very unlikely that XMSSMT-SHA2_40/2_512 will be broken in the forseeable future.
Expand ▾ Collapse ▴

Documentation

Overview

    Atum is a post-quantum secure easy-to-use trusted time-stamping protocol.

    Index

    Constants

    This section is empty.

    Variables

    This section is empty.

    Functions

    func EncodeTimeNonce

    func EncodeTimeNonce(time int64, nonce []byte) []byte

      Pack time and nonce as one byteslice

      func SendRequest

      func SendRequest(serverUrl string, req Request) (*Timestamp, Error)

        Request a timestamp.

        For a simpler interface, use Stamp() or JsonStamp().

        func SetCache

        func SetCache(newCache Cache)

          Set the cache used by the Atum client to store server info and public keys.

          See the Cache interface.

          func Stamp

          func Stamp(serverUrl string, nonce []byte) (*Timestamp, Error)

            Request a timestamp for the given nonce.

            For more flexibility, use SendRequest().

            Types

            type Cache

            type Cache interface {
            
            	// Caches that the given public key is valid for the server
            	StorePublicKey(serverUrl string, alg SignatureAlgorithm,
            		pk []byte, expires time.Time)
            
            	// Returns until when this public key should be trusted for the given
            	// server (and nil if the public key is not to be trusted).
            	GetPublicKey(serverUrl string, alg SignatureAlgorithm, pk []byte) *time.Time
            
            	// Caches the server information.
            	StoreServerInfo(serverUrl string, info ServerInfo)
            
            	//  Retreieves cached server information, if available.
            	GetServerInfo(serverUrl string) *ServerInfo
            }

              Caches for each known Atum server its public keys (for faster verification) and the ServerInfo (for faster stamping).

              type Error

              type Error interface {
              	error
              	Inner() error // Returns the wrapped error, if any
              }

              func JsonStamp

              func JsonStamp(serverUrl string, nonce []byte) ([]byte, Error)

                Request a timestamp for the given nonce and returns it json encoded.

                For more flexibility, use Stamp() or SendRequest().

                func Verify

                func Verify(jsonTs []byte, msgOrNonce []byte) (
                	valid bool, tsTime time.Time, serverUrl string, err Error)

                  Verifies whether a Json encoded timestamp is valid. Returns the server which set the timestamp.

                  NOTE anyone can create a "valid" Atum timestamp by setting up their own

                  server.  You should check that you trust the server.
                  

                  func VerifyFrom

                  func VerifyFrom(jsonTs []byte, msg io.Reader) (
                  	valid bool, tsTime time.Time, serverUrl string, err Error)

                    Like Verify(), but reads the message from an io.Reader.

                    type ErrorCode

                    type ErrorCode string
                    const (
                    	// There is too much lag between the time requested for the timestamp
                    	// and the time at which the request is processed.
                    	ErrorCodeLag ErrorCode = "too much lag"
                    
                    	ErrorMissingNonce ErrorCode = "missing nonce"
                    	ErrorNonceTooLong ErrorCode = "nonce is too long"
                    	ErrorMissingPow   ErrorCode = "proof of work is missing"
                    	ErrorPowInvalid   ErrorCode = "proof of work is invalid"
                    )

                    type Hash

                    type Hash string

                      A possible hash

                      const (
                      	Shake256 Hash = "shake256"
                      )

                      type Hashing

                      type Hashing struct {
                      
                      	// The hash function used to compress the message into a nonce
                      	Hash Hash
                      
                      	// A prefix to hide the hash of the message from the Atum server
                      	Prefix []byte
                      }

                        See the Timestamp.Hashing field

                        func (*Hashing) ComputeNonce

                        func (h *Hashing) ComputeNonce(msg io.Reader) ([]byte, Error)

                          Computes the nonce associated to a message, when hashing is enabled.

                          type PublicKeyCheckResponse

                          type PublicKeyCheckResponse struct {
                          	// Should we trust this public key
                          	Trusted bool
                          
                          	// When should you check again?
                          	Expires time.Time
                          }

                            Response of the Atum server to a public key check

                            type Request

                            type Request struct {
                            
                            	// The nonce to timestamp.  The server might reject the timestamp if it
                            	// is too long.  See ServerInfo.MaxNonceSize.
                            	Nonce []byte
                            
                            	// The proof of work (if required).
                            	//
                            	// THe SendRequest() function will fill this field if it is required by
                            	// ServerInfo.RequiredProofOfWork.
                            	ProofOfWork *pow.Proof
                            
                            	// Unix time to put on the timestamp.  The server will reject the request
                            	// if this time is too far of its own time.  See ServerInfo.AcceptableLag.
                            	Time *int64
                            
                            	// Preferred signature algorithm.  If the specified signature algorithm
                            	// is not supported or this field is omitted, the server will revert
                            	// to the default.
                            	PreferredSigAlg *SignatureAlgorithm
                            }

                              A request to put a timestamp on a nonce.

                              type Response

                              type Response struct {
                              	// Error
                              	Error *ErrorCode
                              
                              	// The timestamp
                              	Stamp *Timestamp
                              
                              	// In case of most errors, the server will include server information.
                              	Info *ServerInfo
                              }

                                The response of the Atum server to a request

                                func (*Response) SetError

                                func (resp *Response) SetError(err ErrorCode)

                                  Convenience function to set the Error field

                                  type ServerInfo

                                  type ServerInfo struct {
                                  	// The maximum size of nonce accepted
                                  	MaxNonceSize int64
                                  
                                  	// Maximum lag to accept in number of seconds
                                  	AcceptableLag int64
                                  
                                  	// Default signature algorithm the server uses
                                  	DefaultSigAlg SignatureAlgorithm
                                  
                                  	// The necessary proof-of-work required for the different signature
                                  	// algorithms.
                                  	RequiredProofOfWork map[SignatureAlgorithm]pow.Request
                                  }

                                    Information published by an Atum server.

                                    type Signature

                                    type Signature struct {
                                    
                                    	// The signature algorithm used
                                    	Alg SignatureAlgorithm
                                    
                                    	// The serialized signature
                                    	Data []byte
                                    
                                    	// The serialized public key with which the signature was set
                                    	PublicKey []byte
                                    }

                                      The signature of the timestamp

                                      func (*Signature) DangerousVerifySignatureButNotPublicKey

                                      func (sig *Signature) DangerousVerifySignatureButNotPublicKey(
                                      	time int64, nonce []byte) (valid bool, err Error)

                                        Verifies the signature on a nonce, but not the public key.

                                        You should only use this function if you have checked the public key should be trusted.

                                        func (Signature) String

                                        func (sig Signature) String() string

                                        type SignatureAlgorithm

                                        type SignatureAlgorithm string

                                          Supported signature algorithms.

                                          const (
                                          	// Ed25519 EdDSA signatures. See rfc8032
                                          	Ed25519 SignatureAlgorithm = "ed25519"
                                          
                                          	// XMSS[MT] signatures.
                                          	// See https://tools.ietf.org/html/draft-irtf-cfrg-xmss-hash-based-signatures-11
                                          	XMSSMT = "xmssmt"
                                          )

                                          type Timestamp

                                          type Timestamp struct {
                                          
                                          	// The unix time at which the timestamp was set
                                          	Time int64
                                          
                                          	// The server by which the timestamp was set
                                          	ServerUrl string
                                          
                                          	// The signature.
                                          	Sig Signature
                                          
                                          	// The Atum server only signs short nonces.  To timestamp a longer message,
                                          	// the Atum server first hashes the long message to a nonce, which
                                          	// in turn is signed by the Atum server.  If this is the case, the following
                                          	// field contains the hash used.
                                          	Hashing *Hashing `json:",omitempty"`
                                          }

                                            A signed timestamp on a nonce or longer message.

                                            The message/nonce are not included.

                                            func (*Timestamp) GetTime

                                            func (ts *Timestamp) GetTime() time.Time

                                              Returns the time at which the timestamp was set.

                                              NOTE Don't forget to Verify() the timestamp!

                                              func (*Timestamp) Verify

                                              func (ts *Timestamp) Verify(msgOrNonce []byte) (valid bool, err Error)

                                                Verifies the timestamp.

                                                NOTE anyone can create a "valid" Atum timestamp by setting up their own

                                                server.  You should check that you trust the server, which is
                                                set in TimeStamp.ServerUrl.
                                                

                                                func (*Timestamp) VerifyFrom

                                                func (ts *Timestamp) VerifyFrom(r io.Reader) (valid bool, err Error)

                                                  Like Verify(), but reads the message from an io.Reader.

                                                  func (*Timestamp) VerifyPublicKey

                                                  func (ts *Timestamp) VerifyPublicKey() (trusted bool, err Error)

                                                    Asks the Atum server if the public key on the signature should be trusted

                                                    Directories

                                                    Path Synopsis
                                                    Create Atum timestamps (server-side).
                                                    Create Atum timestamps (server-side).